History

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

Pull x86 platform driver updates from Hans de Goede: "Highlights: - New think-lmi driver adding support for changing Lenovo Thinkpad BIOS settings from within Linux using the standard firmware- attributes class sysfs API - MS Surface aggregator-cdev now also supports forwarding events to user-space (for debugging / new driver development purposes only) - New intel_skl_int3472 driver this provides the necessary glue to translate ACPI table information to GPIOs, regulators, etc. for camera sensors on Intel devices with IPU3 attached MIPI cameras - A whole bunch of other fixes + device-specific quirk additions - New devm_work_autocancel() devm-helpers.h function" * tag 'platform-drivers-x86-v5.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (83 commits) platform/x86: dell-wmi-sysman: Change user experience when Admin/System Password is modified platform/x86: intel_skl_int3472: Uninitialized variable in skl_int3472_handle_gpio_resources() platform/x86: think-lmi: Move kfree(setting->possible_values) to tlmi_attr_setting_release() platform/x86: think-lmi: Split current_value to reflect only the value platform/x86: think-lmi: Fix issues with duplicate attributes platform/x86: think-lmi: Return EINVAL when kbdlang gets set to a 0 length string platform/x86: intel_cht_int33fe: Move to its own subfolder platform/x86: intel_skl_int3472: Move to intel/ subfolder platform/x86: intel_skl_int3472: Provide skl_int3472_unregister_clock() platform/x86: intel_skl_int3472: Provide skl_int3472_unregister_regulator() platform/x86: intel_skl_int3472: Use ACPI GPIO resource directly platform/x86: intel_skl_int3472: Fix dependencies (drop CLKDEV_LOOKUP) platform/x86: intel_skl_int3472: Free ACPI device resources after use platform/x86: Remove "default n" entries platform/x86: ISST: Use numa node id for cpu pci dev mapping platform/x86: ISST: Optimize CPU to PCI device mapping tools/power/x86/intel-speed-select: v1.10 release tools/power/x86/intel-speed-select: Fix uncore memory frequency display extcon: extcon-max8997: Simplify driver using devm extcon: extcon-max8997: Fix IRQ freeing at error path ...

Linus Torvalds committed 5 years ago

Commit 776ba3a2 parents ebb81c1 0e695c395 files · +4285 −692Browse files

Unified · Split

95 files
  1. Documentation/ABI/testing/sysfs-class-firmware-attributes +17 −1
  2. Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi +55 −0
  3. Documentation/driver-api/surface_aggregator/clients/cdev.rst +122 −5
  4. Documentation/userspace-api/ioctl/ioctl-number.rst +1 −1
  5. MAINTAINERS +22 −3
  6. drivers/acpi/pmic/Kconfig +1 −1
  7. drivers/extcon/extcon-max14577.c +5 −11
  8. drivers/extcon/extcon-max77693.c +5 −12
  9. drivers/extcon/extcon-max8997.c +15 −30
  10. drivers/gpio/Kconfig +1 −1
  11. drivers/gpio/gpio-crystalcove.c +1 −9
  12. drivers/gpio/gpio-wcove.c +19 −20
  13. drivers/gpio/gpiolib-acpi.c +51 −0
  14. drivers/input/touchscreen/goodix.c +0 −52
  15. drivers/mfd/Kconfig +0 −18
  16. drivers/mfd/Makefile +0 −1
  17. drivers/mfd/tps68470.c +0 −97
  18. drivers/platform/surface/aggregator/Kconfig +1 −1
  19. drivers/platform/surface/aggregator/Makefile +1 −1
  20. drivers/platform/surface/aggregator/bus.c +1 −1
  21. drivers/platform/surface/aggregator/bus.h +1 −1
  22. drivers/platform/surface/aggregator/controller.c +271 −61
  23. drivers/platform/surface/aggregator/controller.h +1 −1
  24. drivers/platform/surface/aggregator/core.c +1 −1
  25. drivers/platform/surface/aggregator/ssh_msgb.h +1 −1
  26. drivers/platform/surface/aggregator/ssh_packet_layer.c +4 −8
  27. drivers/platform/surface/aggregator/ssh_packet_layer.h +1 −1
  28. drivers/platform/surface/aggregator/ssh_parser.c +1 −1
  29. drivers/platform/surface/aggregator/ssh_parser.h +1 −1
  30. drivers/platform/surface/aggregator/ssh_request_layer.c +4 −8
  31. drivers/platform/surface/aggregator/ssh_request_layer.h +1 −1
  32. drivers/platform/surface/aggregator/trace.h +1 −1
  33. drivers/platform/surface/surface_aggregator_cdev.c +511 −23
  34. drivers/platform/surface/surface_aggregator_registry.c +12 −35
  35. drivers/platform/x86/Kconfig +21 −28
  36. drivers/platform/x86/Makefile +5 −5
  37. drivers/platform/x86/asus-nb-wmi.c +0 −77
  38. drivers/platform/x86/dell/Kconfig +10 −1
  39. drivers/platform/x86/dell/Makefile +2 −0
  40. drivers/platform/x86/dell/dcdbas.c +1 −2
  41. drivers/platform/x86/dell/dell-laptop.c +10 −3
  42. drivers/platform/x86/dell/dell-wmi-base.c +13 −1
  43. drivers/platform/x86/dell/dell-wmi-privacy.c +391 −0
  44. drivers/platform/x86/dell/dell-wmi-privacy.h +36 −0
  45. drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h +4 −1
  46. drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c +34 −5
  47. drivers/platform/x86/dell/dell-wmi-sysman/int-attributes.c +16 −0
  48. drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c +6 −0
  49. drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c +2 −2
  50. drivers/platform/x86/dell/dell-wmi-sysman/string-attributes.c +15 −1
  51. drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +10 −11
  52. drivers/platform/x86/firmware_attributes_class.c +52 −0
  53. drivers/platform/x86/firmware_attributes_class.h +11 −0
  54. drivers/platform/x86/hdaps.c +1 −1
  55. drivers/platform/x86/hp-wireless.c +0 −102
  56. drivers/platform/x86/ideapad-laptop.c +12 −0
  57. drivers/platform/x86/intel/Kconfig +22 −0
  58. drivers/platform/x86/intel/Makefile +8 −0
  59. drivers/platform/x86/intel/int33fe/Kconfig +24 −0
  60. drivers/platform/x86/intel/int33fe/Makefile +5 −0
  61. drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.c +0 −0
  62. drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.h +0 −0
  63. drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.c +0 −0
  64. drivers/platform/x86/intel/int33fe/intel_cht_int33fe_typec.c +2 −2
  65. drivers/platform/x86/intel/int3472/Kconfig +30 −0
  66. drivers/platform/x86/intel/int3472/Makefile +5 −0
  67. drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c +207 −0
  68. drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c +106 −0
  69. drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h +122 −0
  70. drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c +413 −0
  71. drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c +137 −0
  72. drivers/platform/x86/intel_ips.c +1 −1
  73. drivers/platform/x86/intel_pmt_crashlog.c +1 −1
  74. drivers/platform/x86/intel_speed_select_if/isst_if_common.c +68 −5
  75. drivers/platform/x86/samsung-laptop.c +14 −21
  76. drivers/platform/x86/tc1100-wmi.c +1 −1
  77. drivers/platform/x86/think-lmi.c +904 −0
  78. drivers/platform/x86/think-lmi.h +72 −0
  79. drivers/platform/x86/thinkpad_acpi.c +1 −1
  80. drivers/platform/x86/toshiba_acpi.c +1 −0
  81. drivers/platform/x86/toshiba_haps.c +1 −1
  82. drivers/platform/x86/touchscreen_dmi.c +86 −1
  83. drivers/platform/x86/uv_sysfs.c +2 −2
  84. drivers/platform/x86/wireless-hotkey.c +103 −0
  85. include/linux/acpi.h +7 −0
  86. include/linux/devm-helpers.h +25 −0
  87. include/linux/gpio/consumer.h +2 −0
  88. include/linux/surface_aggregator/controller.h +26 −1
  89. include/linux/surface_aggregator/device.h +1 −1
  90. include/linux/surface_aggregator/serial_hub.h +1 −1
  91. include/uapi/linux/surface_aggregator/cdev.h +71 −2
  92. tools/power/x86/intel-speed-select/isst-config.c +17 −1
  93. tools/power/x86/intel-speed-select/isst-core.c +15 −0
  94. tools/power/x86/intel-speed-select/isst-display.c +1 −1
  95. tools/power/x86/intel-speed-select/isst.h +2 −0
Documentation/ABI/testing/sysfs-class-firmware-attributes+17−1
@@ -197,8 +197,24 @@ Description: · 196 unmodified lines
197197 Drivers may emit a CHANGE uevent when a password is set or unset
198198 userspace may check it again.
199199
200− On Dell systems, if Admin password is set, then all BIOS attributes
200+ On Dell and Lenovo systems, if Admin password is set, then all BIOS attributes
201201 require password validation.
202+ On Lenovo systems if you change the Admin password the new password is not active until
203+ the next boot.
204+
205+ Lenovo specific class extensions
206+ ------------------------------
207+
208+ On Lenovo systems the following additional settings are available:
209+
210+ lenovo_encoding:
211+ The encoding method that is used. This can be either "ascii"
212+ or "scancode". Default is set to "ascii"
213+
214+ lenovo_kbdlang:
215+ The keyboard language method that is used. This is generally a
216+ two char code (e.g. "us", "fr", "gr") and may vary per platform.
217+ Default is set to "us"
202218
203219 What: /sys/class/firmware-attributes/*/attributes/pending_reboot
204220 Date: February 2021
Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi+55−0
@@ -0,0 +1,55 @@
1+What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type
2+Date: Apr 2021
3+KernelVersion: 5.13
4+Contact: "perry.yuan@dell.com>"
5+Description:
6+ Display which dell hardware level privacy devices are supported
7+ “Dell Privacy” is a set of HW, FW, and SW features to enhance
8+ Dell’s commitment to platform privacy for MIC, Camera, and
9+ ePrivacy screens.
10+ The supported hardware privacy devices are:
11+Attributes:
12+ Microphone Mute:
13+ Identifies the local microphone can be muted by hardware, no applications
14+ is available to capture system mic sound
15+
16+ Camera Shutter:
17+ Identifies camera shutter controlled by hardware, which is a micromechanical
18+ shutter assembly that is built onto the camera module to block capturing images
19+ from outside the laptop
20+
21+ supported:
22+ The privacy device is supported by this system
23+
24+ unsupported:
25+ The privacy device is not supported on this system
26+
27+ For example to check which privacy devices are supported:
28+
29+ # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type
30+ [Microphone Mute] [supported]
31+ [Camera Shutter] [supported]
32+ [ePrivacy Screen] [unsupported]
33+
34+What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state
35+Date: Apr 2021
36+KernelVersion: 5.13
37+Contact: "perry.yuan@dell.com>"
38+Description:
39+ Allow user space to check current dell privacy device state.
40+ Describes the Device State class exposed by BIOS which can be
41+ consumed by various applications interested in knowing the Privacy
42+ feature capabilities
43+Attributes:
44+ muted:
45+ Identifies the privacy device is turned off and cannot send stream to OS applications
46+
47+ unmuted:
48+ Identifies the privacy device is turned on ,audio or camera driver can get
49+ stream from mic and camera module to OS applications
50+
51+ For example to check all supported current privacy device states:
52+
53+ # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state
54+ [Microphone] [unmuted]
55+ [Camera Shutter] [unmuted]
Documentation/driver-api/surface_aggregator/clients/cdev.rst+122−5
@@ -1,9 +1,8 @@
11 .. SPDX-License-Identifier: GPL-2.0+
22
3−.. |u8| replace:: :c:type:`u8 <u8>`
4−.. |u16| replace:: :c:type:`u16 <u16>`
53 .. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>`
64 .. |ssam_cdev_request_flags| replace:: :c:type:`enum ssam_cdev_request_flags <ssam_cdev_request_flags>`
5+.. |ssam_cdev_event| replace:: :c:type:`struct ssam_cdev_event <ssam_cdev_event>`
76
87 ==============================
98 User-Space EC Interface (cdev)
@@ -23,6 +22,40 @@ These IOCTLs and their respective input/output parameter structs are defined in · 13 unmodified lines
2322 A small python library and scripts for accessing this interface can be found
2423 at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam.
2524
25+.. contents::
26+
27+
28+Receiving Events
29+================
30+
31+Events can be received by reading from the device-file. The are represented by
32+the |ssam_cdev_event| datatype.
33+
34+Before events are available to be read, however, the desired notifiers must be
35+registered via the ``SSAM_CDEV_NOTIF_REGISTER`` IOCTL. Notifiers are, in
36+essence, callbacks, called when the EC sends an event. They are, in this
37+interface, associated with a specific target category and device-file-instance.
38+They forward any event of this category to the buffer of the corresponding
39+instance, from which it can then be read.
40+
41+Notifiers themselves do not enable events on the EC. Thus, it may additionally
42+be necessary to enable events via the ``SSAM_CDEV_EVENT_ENABLE`` IOCTL. While
43+notifiers work per-client (i.e. per-device-file-instance), events are enabled
44+globally, for the EC and all of its clients (regardless of userspace or
45+non-userspace). The ``SSAM_CDEV_EVENT_ENABLE`` and ``SSAM_CDEV_EVENT_DISABLE``
46+IOCTLs take care of reference counting the events, such that an event is
47+enabled as long as there is a client that has requested it.
48+
49+Note that enabled events are not automatically disabled once the client
50+instance is closed. Therefore any client process (or group of processes) should
51+balance their event enable calls with the corresponding event disable calls. It
52+is, however, perfectly valid to enable and disable events on different client
53+instances. For example, it is valid to set up notifiers and read events on
54+client instance ``A``, enable those events on instance ``B`` (note that these
55+will also be received by A since events are enabled/disabled globally), and
56+after no more events are desired, disable the previously enabled events via
57+instance ``C``.
58+
2659
2760 Controller IOCTLs
2861 =================
@@ -45,9 +78,33 @@ The following IOCTLs are provided: · 16 unmodified lines
4578 - ``REQUEST``
4679 - Perform synchronous SAM request.
4780
81+ * - ``0xA5``
82+ - ``2``
83+ - ``W``
84+ - ``NOTIF_REGISTER``
85+ - Register event notifier.
4886
49−``REQUEST``
50−-----------
87+ * - ``0xA5``
88+ - ``3``
89+ - ``W``
90+ - ``NOTIF_UNREGISTER``
91+ - Unregister event notifier.
92+
93+ * - ``0xA5``
94+ - ``4``
95+ - ``W``
96+ - ``EVENT_ENABLE``
97+ - Enable event source.
98+
99+ * - ``0xA5``
100+ - ``5``
101+ - ``W``
102+ - ``EVENT_DISABLE``
103+ - Disable event source.
104+
105+
106+``SSAM_CDEV_REQUEST``
107+---------------------
51108
52109 Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.
53110
@@ -82,6 +139,66 @@ submitted, and completed (i.e. handed back to user-space) successfully from · 28 unmodified lines
82139 inside the IOCTL, but the request ``status`` member may still be negative in
83140 case the actual execution of the request failed after it has been submitted.
84141
85−A full definition of the argument struct is provided below:
142+A full definition of the argument struct is provided below.
143+
144+``SSAM_CDEV_NOTIF_REGISTER``
145+----------------------------
146+
147+Defined as ``_IOW(0xA5, 2, struct ssam_cdev_notifier_desc)``.
148+
149+Register a notifier for the event target category specified in the given
150+notifier description with the specified priority. Notifiers registration is
151+required to receive events, but does not enable events themselves. After a
152+notifier for a specific target category has been registered, all events of that
153+category will be forwarded to the userspace client and can then be read from
154+the device file instance. Note that events may have to be enabled, e.g. via the
155+``SSAM_CDEV_EVENT_ENABLE`` IOCTL, before the EC will send them.
156+
157+Only one notifier can be registered per target category and client instance. If
158+a notifier has already been registered, this IOCTL will fail with ``-EEXIST``.
159+
160+Notifiers will automatically be removed when the device file instance is
161+closed.
162+
163+``SSAM_CDEV_NOTIF_UNREGISTER``
164+------------------------------
165+
166+Defined as ``_IOW(0xA5, 3, struct ssam_cdev_notifier_desc)``.
167+
168+Unregisters the notifier associated with the specified target category. The
169+priority field will be ignored by this IOCTL. If no notifier has been
170+registered for this client instance and the given category, this IOCTL will
171+fail with ``-ENOENT``.
172+
173+``SSAM_CDEV_EVENT_ENABLE``
174+--------------------------
175+
176+Defined as ``_IOW(0xA5, 4, struct ssam_cdev_event_desc)``.
177+
178+Enable the event associated with the given event descriptor.
179+
180+Note that this call will not register a notifier itself, it will only enable
181+events on the controller. If you want to receive events by reading from the
182+device file, you will need to register the corresponding notifier(s) on that
183+instance.
184+
185+Events are not automatically disabled when the device file is closed. This must
186+be done manually, via a call to the ``SSAM_CDEV_EVENT_DISABLE`` IOCTL.
187+
188+``SSAM_CDEV_EVENT_DISABLE``
189+---------------------------
190+
191+Defined as ``_IOW(0xA5, 5, struct ssam_cdev_event_desc)``.
192+
193+Disable the event associated with the given event descriptor.
194+
195+Note that this will not unregister any notifiers. Events may still be received
196+and forwarded to user-space after this call. The only safe way of stopping
197+events from being received is unregistering all previously registered
198+notifiers.
199+
200+
201+Structures and Enums
202+====================
86203
87204 .. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
Documentation/userspace-api/ioctl/ioctl-number.rst+1−1
@@ -325,7 +325,7 @@ Code Seq# Include File Comments · 324 unmodified lines
325325 0xA3 90-9F linux/dtlk.h
326326 0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
327327 0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
328−0xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
328+0xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
329329 <mailto:luzmaximilian@gmail.com>
330330 0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver
331331 <mailto:luzmaximilian@gmail.com>
MAINTAINERS+22−3
@@ -5187,7 +5187,14 @@ DELL WMI NOTIFICATIONS DRIVER · 5186 unmodified lines
51875187 M: Matthew Garrett <mjg59@srcf.ucam.org>
51885188 M: Pali Rohár <pali@kernel.org>
51895189 S: Maintained
5190−F: drivers/platform/x86/dell/dell-wmi.c
5190+F: drivers/platform/x86/dell/dell-wmi-base.c
5191+
5192+DELL WMI HARDWARE PRIVACY SUPPORT
5193+M: Perry Yuan <Perry.Yuan@dell.com>
5194+L: Dell.Client.Kernel@dell.com
5195+L: platform-driver-x86@vger.kernel.org
5196+S: Maintained
5197+F: drivers/platform/x86/dell/dell-wmi-privacy.c
51915198
51925199 DELTA ST MEDIA DRIVER
51935200 M: Hugues Fruchet <hugues.fruchet@foss.st.com>
@@ -9397,6 +9404,11 @@ S: Maintained · 4203 unmodified lines
93979404 F: arch/x86/include/asm/intel_scu_ipc.h
93989405 F: drivers/platform/x86/intel_scu_*
93999406
9407+INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
9408+M: Daniel Scally <djrscally@gmail.com>
9409+S: Maintained
9410+F: drivers/platform/x86/intel/int3472/
9411+
94009412 INTEL SPEED SELECT TECHNOLOGY
94019413 M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
94029414 L: platform-driver-x86@vger.kernel.org
@@ -9417,7 +9429,7 @@ F: include/linux/firmware/intel/stratix10-smc.h · 14 unmodified lines
94179429 F: include/linux/firmware/intel/stratix10-svc-client.h
94189430
94199431 INTEL TELEMETRY DRIVER
9420−M: Rajneesh Bhardwaj <rajneesh.bhardwaj@linux.intel.com>
9432+M: Rajneesh Bhardwaj <irenic.rajneesh@gmail.com>
94219433 M: "David E. Box" <david.e.box@linux.intel.com>
94229434 L: platform-driver-x86@vger.kernel.org
94239435 S: Maintained
@@ -12209,7 +12221,7 @@ M: Maximilian Luz <luzmaximilian@gmail.com> · 2785 unmodified lines
1220912221 L: platform-driver-x86@vger.kernel.org
1221012222 S: Maintained
1221112223 W: https://github.com/linux-surface/surface-aggregator-module
12212−C: irc://chat.freenode.net/##linux-surface
12224+C: irc://irc.libera.chat/linux-surface
1221312225 F: Documentation/driver-api/surface_aggregator/
1221412226 F: drivers/platform/surface/aggregator/
1221512227 F: drivers/platform/surface/surface_acpi_notify.c
@@ -18215,6 +18227,13 @@ W: http://thinkwiki.org/wiki/Ibm-acpi · 5999 unmodified lines
1821518227 T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
1821618228 F: drivers/platform/x86/thinkpad_acpi.c
1821718229
18230+THINKPAD LMI DRIVER
18231+M: Mark Pearson <markpearson@lenovo.com>
18232+L: platform-driver-x86@vger.kernel.org
18233+S: Maintained
18234+F: Documentation/ABI/testing/sysfs-class-firmware-attributes
18235+F: drivers/platform/x86/think-lmi.?
18236+
1821818237 THUNDERBOLT DMA TRAFFIC TEST DRIVER
1821918238 M: Isaac Hazan <isaac.hazan@intel.com>
1822018239 L: linux-usb@vger.kernel.org
drivers/acpi/pmic/Kconfig+1−1
@@ -52,7 +52,7 @@ endif # PMIC_OPREGION · 51 unmodified lines
5252
5353 config TPS68470_PMIC_OPREGION
5454 bool "ACPI operation region support for TPS68470 PMIC"
55− depends on MFD_TPS68470
55+ depends on INTEL_SKL_INT3472
5656 help
5757 This config adds ACPI operation region support for TI TPS68470 PMIC.
5858 TPS68470 device is an advanced power management unit that powers
drivers/extcon/extcon-max14577.c+5−11
@@ -6,6 +6,7 @@ · 5 unmodified lines
66 // Chanwoo Choi <cw00.choi@samsung.com>
77 // Krzysztof Kozlowski <krzk@kernel.org>
88
9+#include <linux/devm-helpers.h>
910 #include <linux/kernel.h>
1011 #include <linux/module.h>
1112 #include <linux/i2c.h>
@@ -673,7 +674,10 @@ static int max14577_muic_probe(struct platform_device *pdev) · 661 unmodified lines
673674 platform_set_drvdata(pdev, info);
674675 mutex_init(&info->mutex);
675676
676− INIT_WORK(&info->irq_work, max14577_muic_irq_work);
677+ ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
678+ max14577_muic_irq_work);
679+ if (ret)
680+ return ret;
677681
678682 switch (max14577->dev_type) {
679683 case MAXIM_DEVICE_TYPE_MAX77836:
@@ -766,15 +770,6 @@ static int max14577_muic_probe(struct platform_device *pdev) · 86 unmodified lines
766770 return ret;
767771 }
768772
769−static int max14577_muic_remove(struct platform_device *pdev)
770−{
771− struct max14577_muic_info *info = platform_get_drvdata(pdev);
772−
773− cancel_work_sync(&info->irq_work);
774−
775− return 0;
776−}
777−
778773 static const struct platform_device_id max14577_muic_id[] = {
779774 { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
780775 { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
@@ -797,7 +792,6 @@ static struct platform_driver max14577_muic_driver = { · 16 unmodified lines
797792 .of_match_table = of_max14577_muic_dt_match,
798793 },
799794 .probe = max14577_muic_probe,
800− .remove = max14577_muic_remove,
801795 .id_table = max14577_muic_id,
802796 };
803797
drivers/extcon/extcon-max77693.c+5−12
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 // Copyright (C) 2012 Samsung Electrnoics
66 // Chanwoo Choi <cw00.choi@samsung.com>
77
8+#include <linux/devm-helpers.h>
89 #include <linux/kernel.h>
910 #include <linux/module.h>
1011 #include <linux/i2c.h>
@@ -1127,7 +1128,10 @@ static int max77693_muic_probe(struct platform_device *pdev) · 1116 unmodified lines
11271128 platform_set_drvdata(pdev, info);
11281129 mutex_init(&info->mutex);
11291130
1130− INIT_WORK(&info->irq_work, max77693_muic_irq_work);
1131+ ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
1132+ max77693_muic_irq_work);
1133+ if (ret)
1134+ return ret;
11311135
11321136 /* Support irq domain for MAX77693 MUIC device */
11331137 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
@@ -1254,22 +1258,11 @@ static int max77693_muic_probe(struct platform_device *pdev) · 120 unmodified lines
12541258 return ret;
12551259 }
12561260
1257−static int max77693_muic_remove(struct platform_device *pdev)
1258−{
1259− struct max77693_muic_info *info = platform_get_drvdata(pdev);
1260−
1261− cancel_work_sync(&info->irq_work);
1262− input_unregister_device(info->dock);
1263−
1264− return 0;
1265−}
1266−
12671261 static struct platform_driver max77693_muic_driver = {
12681262 .driver = {
12691263 .name = DEV_NAME,
12701264 },
12711265 .probe = max77693_muic_probe,
1272− .remove = max77693_muic_remove,
12731266 };
12741267
12751268 module_platform_driver(max77693_muic_driver);
drivers/extcon/extcon-max8997.c+15−30
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 // Copyright (C) 2012 Samsung Electronics
66 // Donggeun Kim <dg77.kim@samsung.com>
77
8+#include <linux/devm-helpers.h>
89 #include <linux/kernel.h>
910 #include <linux/module.h>
1011 #include <linux/i2c.h>
@@ -650,27 +651,30 @@ static int max8997_muic_probe(struct platform_device *pdev) · 639 unmodified lines
650651 mutex_init(&info->mutex);
651652
652653 INIT_WORK(&info->irq_work, max8997_muic_irq_work);
654+ ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
655+ max8997_muic_irq_work);
656+ if (ret)
657+ return ret;
653658
654659 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
655660 struct max8997_muic_irq *muic_irq = &muic_irqs[i];
656661 unsigned int virq = 0;
657662
658663 virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
659− if (!virq) {
660− ret = -EINVAL;
661− goto err_irq;
662− }
664+ if (!virq)
665+ return -EINVAL;
666+
663667 muic_irq->virq = virq;
664668
665− ret = request_threaded_irq(virq, NULL,
666− max8997_muic_irq_handler,
667− IRQF_NO_SUSPEND,
668− muic_irq->name, info);
669+ ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
670+ max8997_muic_irq_handler,
671+ IRQF_NO_SUSPEND,
672+ muic_irq->name, info);
669673 if (ret) {
670674 dev_err(&pdev->dev,
671675 "failed: irq request (IRQ: %d, error :%d)\n",
672676 muic_irq->irq, ret);
673− goto err_irq;
677+ return ret;
674678 }
675679 }
676680
@@ -678,14 +682,13 @@ static int max8997_muic_probe(struct platform_device *pdev) · 1 unmodified line
678682 info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
679683 if (IS_ERR(info->edev)) {
680684 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
681− ret = PTR_ERR(info->edev);
682− goto err_irq;
685+ return PTR_ERR(info->edev);
683686 }
684687
685688 ret = devm_extcon_dev_register(&pdev->dev, info->edev);
686689 if (ret) {
687690 dev_err(&pdev->dev, "failed to register extcon device\n");
688− goto err_irq;
691+ return ret;
689692 }
690693
691694 if (pdata && pdata->muic_pdata) {
@@ -756,23 +759,6 @@ static int max8997_muic_probe(struct platform_device *pdev) · 64 unmodified lines
756759 delay_jiffies);
757760
758761 return 0;
759−
760−err_irq:
761− while (--i >= 0)
762− free_irq(muic_irqs[i].virq, info);
763− return ret;
764−}
765−
766−static int max8997_muic_remove(struct platform_device *pdev)
767−{
768− struct max8997_muic_info *info = platform_get_drvdata(pdev);
769− int i;
770−
771− for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
772− free_irq(muic_irqs[i].virq, info);
773− cancel_work_sync(&info->irq_work);
774−
775− return 0;
776762 }
777763
778764 static struct platform_driver max8997_muic_driver = {
@@ -780,7 +766,6 @@ static struct platform_driver max8997_muic_driver = { · 1 unmodified line
780766 .name = DEV_NAME,
781767 },
782768 .probe = max8997_muic_probe,
783− .remove = max8997_muic_remove,
784769 };
785770
786771 module_platform_driver(max8997_muic_driver);
drivers/gpio/Kconfig+1−1
@@ -1367,7 +1367,7 @@ config GPIO_TPS65912 · 1366 unmodified lines
13671367
13681368 config GPIO_TPS68470
13691369 bool "TPS68470 GPIO"
1370− depends on MFD_TPS68470
1370+ depends on INTEL_SKL_INT3472
13711371 help
13721372 Select this option to enable GPIO driver for the TPS68470
13731373 chip family.
drivers/gpio/gpio-crystalcove.c+1−9
@@ -339,8 +339,6 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) · 338 unmodified lines
339339 if (!cg)
340340 return -ENOMEM;
341341
342− platform_set_drvdata(pdev, cg);
343−
344342 mutex_init(&cg->buslock);
345343 cg->chip.label = KBUILD_MODNAME;
346344 cg->chip.direction_input = crystalcove_gpio_dir_in;
@@ -372,13 +370,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) · 25 unmodified lines
372370 return retval;
373371 }
374372
375− retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
376− if (retval) {
377− dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval);
378− return retval;
379− }
380−
381− return 0;
373+ return devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);
382374 }
383375
384376 static struct platform_driver crystalcove_gpio_driver = {
drivers/gpio/gpio-wcove.c+19−20
@@ -99,19 +99,14 @@ struct wcove_gpio { · 98 unmodified lines
9999 bool set_irq_mask;
100100 };
101101
102−static inline int to_reg(int gpio, enum ctrl_register reg_type)
102+static inline int to_reg(int gpio, enum ctrl_register type)
103103 {
104− unsigned int reg;
104+ unsigned int reg = type == CTRL_IN ? GPIO_IN_CTRL_BASE : GPIO_OUT_CTRL_BASE;
105105
106106 if (gpio >= WCOVE_GPIO_NUM)
107107 return -EOPNOTSUPP;
108108
109− if (reg_type == CTRL_IN)
110− reg = GPIO_IN_CTRL_BASE + gpio;
111− else
112− reg = GPIO_OUT_CTRL_BASE + gpio;
113−
114− return reg;
109+ return reg + gpio;
115110 }
116111
117112 static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask)
@@ -129,7 +124,7 @@ static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask) · 11 unmodified lines
129124 return reg;
130125 }
131126
132−static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio)
127+static void wcove_update_irq_mask(struct wcove_gpio *wg, irq_hw_number_t gpio)
133128 {
134129 unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask);
135130
@@ -139,13 +134,10 @@ static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) · 3 unmodified lines
139134 regmap_clear_bits(wg->regmap, reg, mask);
140135 }
141136
142−static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)
137+static void wcove_update_irq_ctrl(struct wcove_gpio *wg, irq_hw_number_t gpio)
143138 {
144139 int reg = to_reg(gpio, CTRL_IN);
145140
146− if (reg < 0)
147− return;
148−
149141 regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt);
150142 }
151143
@@ -248,8 +240,9 @@ static int wcove_irq_type(struct irq_data *data, unsigned int type) · 96 unmodified lines
248240 {
249241 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
250242 struct wcove_gpio *wg = gpiochip_get_data(chip);
243+ irq_hw_number_t gpio = irqd_to_hwirq(data);
251244
252− if (data->hwirq >= WCOVE_GPIO_NUM)
245+ if (gpio >= WCOVE_GPIO_NUM)
253246 return 0;
254247
255248 switch (type) {
@@ -286,7 +279,7 @@ static void wcove_bus_sync_unlock(struct irq_data *data) · 30 unmodified lines
286279 {
287280 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
288281 struct wcove_gpio *wg = gpiochip_get_data(chip);
289− int gpio = data->hwirq;
282+ irq_hw_number_t gpio = irqd_to_hwirq(data);
290283
291284 if (wg->update & UPDATE_IRQ_TYPE)
292285 wcove_update_irq_ctrl(wg, gpio);
@@ -301,8 +294,9 @@ static void wcove_irq_unmask(struct irq_data *data) · 8 unmodified lines
301294 {
302295 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
303296 struct wcove_gpio *wg = gpiochip_get_data(chip);
297+ irq_hw_number_t gpio = irqd_to_hwirq(data);
304298
305− if (data->hwirq >= WCOVE_GPIO_NUM)
299+ if (gpio >= WCOVE_GPIO_NUM)
306300 return;
307301
308302 wg->set_irq_mask = false;
@@ -313,8 +307,9 @@ static void wcove_irq_mask(struct irq_data *data) · 4 unmodified lines
313307 {
314308 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
315309 struct wcove_gpio *wg = gpiochip_get_data(chip);
310+ irq_hw_number_t gpio = irqd_to_hwirq(data);
316311
317− if (data->hwirq >= WCOVE_GPIO_NUM)
312+ if (gpio >= WCOVE_GPIO_NUM)
318313 return;
319314
320315 wg->set_irq_mask = true;
@@ -369,8 +364,7 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) · 48 unmodified lines
369364 return IRQ_HANDLED;
370365 }
371366
372−static void wcove_gpio_dbg_show(struct seq_file *s,
373− struct gpio_chip *chip)
367+static void wcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
374368 {
375369 unsigned int ctlo, ctli, irq_mask, irq_status;
376370 struct wcove_gpio *wg = gpiochip_get_data(chip);
@@ -379,10 +373,15 @@ static void wcove_gpio_dbg_show(struct seq_file *s, · 2 unmodified lines
379373 for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) {
380374 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
381375 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli);
376+ if (ret) {
377+ dev_err(wg->dev, "Failed to read registers: CTRL out/in\n");
378+ break;
379+ }
380+
382381 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask);
383382 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status);
384383 if (ret) {
385− pr_err("Failed to read registers: ctrl out/in or irq status/mask\n");
384+ dev_err(wg->dev, "Failed to read registers: IRQ status/mask\n");
386385 break;
387386 }
388387
drivers/gpio/gpiolib-acpi.c+51−0
@@ -128,6 +128,34 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin) · 127 unmodified lines
128128 return gpiochip_get_desc(chip, pin);
129129 }
130130
131+/**
132+ * acpi_get_and_request_gpiod - Translate ACPI GPIO pin to GPIO descriptor and
133+ * hold a refcount to the GPIO device.
134+ * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
135+ * @pin: ACPI GPIO pin number (0-based, controller-relative)
136+ * @label: Label to pass to gpiod_request()
137+ *
138+ * This function is a simple pass-through to acpi_get_gpiod(), except that
139+ * as it is intended for use outside of the GPIO layer (in a similar fashion to
140+ * gpiod_get_index() for example) it also holds a reference to the GPIO device.
141+ */
142+struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label)
143+{
144+ struct gpio_desc *gpio;
145+ int ret;
146+
147+ gpio = acpi_get_gpiod(path, pin);
148+ if (IS_ERR(gpio))
149+ return gpio;
150+
151+ ret = gpiod_request(gpio, label);
152+ if (ret)
153+ return ERR_PTR(ret);
154+
155+ return gpio;
156+}
157+EXPORT_SYMBOL_GPL(acpi_get_and_request_gpiod);
158+
131159 static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
132160 {
133161 struct acpi_gpio_event *event = data;
@@ -168,6 +196,29 @@ bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, · 34 unmodified lines
168196 }
169197 EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);
170198
199+/**
200+ * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO
201+ * I/O resource or return False if not.
202+ * @ares: Pointer to the ACPI resource to fetch
203+ * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer
204+ */
205+bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
206+ struct acpi_resource_gpio **agpio)
207+{
208+ struct acpi_resource_gpio *gpio;
209+
210+ if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
211+ return false;
212+
213+ gpio = &ares->data.gpio;
214+ if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO)
215+ return false;
216+
217+ *agpio = gpio;
218+ return true;
219+}
220+EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource);
221+
171222 static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
172223 struct acpi_gpio_event *event)
173224 {
drivers/input/touchscreen/goodix.c+0−52
@@ -178,51 +178,6 @@ static const unsigned long goodix_irq_flags[] = { · 177 unmodified lines
178178 IRQ_TYPE_LEVEL_HIGH,
179179 };
180180
181−/*
182− * Those tablets have their coordinates origin at the bottom right
183− * of the tablet, as if rotated 180 degrees
184− */
185−static const struct dmi_system_id rotated_screen[] = {
186−#if defined(CONFIG_DMI) && defined(CONFIG_X86)
187− {
188− .ident = "Teclast X89",
189− .matches = {
190− /* tPAD is too generic, also match on bios date */
191− DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
192− DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
193− DMI_MATCH(DMI_BIOS_DATE, "12/19/2014"),
194− },
195− },
196− {
197− .ident = "Teclast X98 Pro",
198− .matches = {
199− /*
200− * Only match BIOS date, because the manufacturers
201− * BIOS does not report the board name at all
202− * (sometimes)...
203− */
204− DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
205− DMI_MATCH(DMI_BIOS_DATE, "10/28/2015"),
206− },
207− },
208− {
209− .ident = "WinBook TW100",
210− .matches = {
211− DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
212− DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
213− }
214− },
215− {
216− .ident = "WinBook TW700",
217− .matches = {
218− DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
219− DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
220− },
221− },
222−#endif
223− {}
224−};
225−
226181 static const struct dmi_system_id nine_bytes_report[] = {
227182 #if defined(CONFIG_DMI) && defined(CONFIG_X86)
228183 {
@@ -1123,13 +1078,6 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) · 894 unmodified lines
11231078 ABS_MT_POSITION_Y, ts->prop.max_y);
11241079 }
11251080
1126− if (dmi_check_system(rotated_screen)) {
1127− ts->prop.invert_x = true;
1128− ts->prop.invert_y = true;
1129− dev_dbg(&ts->client->dev,
1130− "Applying '180 degrees rotated screen' quirk\n");
1131− }
1132−
11331081 if (dmi_check_system(nine_bytes_report)) {
11341082 ts->contact_size = 9;
11351083
drivers/mfd/Kconfig+0−18
@@ -1499,24 +1499,6 @@ config MFD_TPS65217 · 1498 unmodified lines
14991499 This driver can also be built as a module. If so, the module
15001500 will be called tps65217.
15011501
1502−config MFD_TPS68470
1503− bool "TI TPS68470 Power Management / LED chips"
1504− depends on ACPI && PCI && I2C=y
1505− depends on I2C_DESIGNWARE_PLATFORM=y
1506− select MFD_CORE
1507− select REGMAP_I2C
1508− help
1509− If you say yes here you get support for the TPS68470 series of
1510− Power Management / LED chips.
1511−
1512− These include voltage regulators, LEDs and other features
1513− that are often used in portable devices.
1514−
1515− This option is a bool as it provides an ACPI operation
1516− region, which must be available before any of the devices
1517− using this are probed. This option also configures the
1518− designware-i2c driver to be built-in, for the same reason.
1519−
15201502 config MFD_TI_LP873X
15211503 tristate "TI LP873X Power Management IC"
15221504 depends on I2C
drivers/mfd/Makefile+0−1
@@ -105,7 +105,6 @@ obj-$(CONFIG_MFD_TPS65910) += tps65910.o · 104 unmodified lines
105105 obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o
106106 obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
107107 obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
108−obj-$(CONFIG_MFD_TPS68470) += tps68470.o
109108 obj-$(CONFIG_MFD_TPS80031) += tps80031.o
110109 obj-$(CONFIG_MENELAUS) += menelaus.o
111110
drivers/mfd/tps68470.c+0−97
@@ -1,97 +0,0 @@
1−// SPDX-License-Identifier: GPL-2.0
2−/*
3− * TPS68470 chip Parent driver
4− *
5− * Copyright (C) 2017 Intel Corporation
6− *
7− * Authors:
8− * Rajmohan Mani <rajmohan.mani@intel.com>
9− * Tianshu Qiu <tian.shu.qiu@intel.com>
10− * Jian Xu Zheng <jian.xu.zheng@intel.com>
11− * Yuning Pu <yuning.pu@intel.com>
12− */
13−
14−#include <linux/acpi.h>
15−#include <linux/delay.h>
16−#include <linux/i2c.h>
17−#include <linux/init.h>
18−#include <linux/mfd/core.h>
19−#include <linux/mfd/tps68470.h>
20−#include <linux/regmap.h>
21−
22−static const struct mfd_cell tps68470s[] = {
23− { .name = "tps68470-gpio" },
24− { .name = "tps68470_pmic_opregion" },
25−};
26−
27−static const struct regmap_config tps68470_regmap_config = {
28− .reg_bits = 8,
29− .val_bits = 8,
30− .max_register = TPS68470_REG_MAX,
31−};
32−
33−static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
34−{
35− unsigned int version;
36− int ret;
37−
38− /* Force software reset */
39− ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
40− if (ret)
41− return ret;
42−
43− ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
44− if (ret) {
45− dev_err(dev, "Failed to read revision register: %d\n", ret);
46− return ret;
47− }
48−
49− dev_info(dev, "TPS68470 REVID: 0x%x\n", version);
50−
51− return 0;
52−}
53−
54−static int tps68470_probe(struct i2c_client *client)
55−{
56− struct device *dev = &client->dev;
57− struct regmap *regmap;
58− int ret;
59−
60− regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
61− if (IS_ERR(regmap)) {
62− dev_err(dev, "devm_regmap_init_i2c Error %ld\n",
63− PTR_ERR(regmap));
64− return PTR_ERR(regmap);
65− }
66−
67− i2c_set_clientdata(client, regmap);
68−
69− ret = tps68470_chip_init(dev, regmap);
70− if (ret < 0) {
71− dev_err(dev, "TPS68470 Init Error %d\n", ret);
72− return ret;
73− }
74−
75− ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, tps68470s,
76− ARRAY_SIZE(tps68470s), NULL, 0, NULL);
77− if (ret < 0) {
78− dev_err(dev, "devm_mfd_add_devices failed: %d\n", ret);
79− return ret;
80− }
81−
82− return 0;
83−}
84−
85−static const struct acpi_device_id tps68470_acpi_ids[] = {
86− {"INT3472"},
87− {},
88−};
89−
90−static struct i2c_driver tps68470_driver = {
91− .driver = {
92− .name = "tps68470",
93− .acpi_match_table = tps68470_acpi_ids,
94− },
95− .probe_new = tps68470_probe,
96−};
97−builtin_i2c_driver(tps68470_driver);
drivers/platform/surface/aggregator/Kconfig+1−1
@@ -1,5 +1,5 @@
11 # SPDX-License-Identifier: GPL-2.0+
2−# Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
2+# Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
33
44 menuconfig SURFACE_AGGREGATOR
55 tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
drivers/platform/surface/aggregator/Makefile+1−1
@@ -1,5 +1,5 @@
11 # SPDX-License-Identifier: GPL-2.0+
2−# Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
2+# Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
33
44 # For include/trace/define_trace.h to include trace.h
55 CFLAGS_core.o = -I$(src)
drivers/platform/surface/aggregator/bus.c+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * Surface System Aggregator Module bus and device integration.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #include <linux/device.h>
drivers/platform/surface/aggregator/bus.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * Surface System Aggregator Module bus and device integration.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #ifndef _SURFACE_AGGREGATOR_BUS_H
drivers/platform/surface/aggregator/controller.c+271−61
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * Main SSAM/SSH controller structure and functionality.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #include <linux/acpi.h>
@@ -407,6 +407,31 @@ ssam_nf_refcount_dec(struct ssam_nf *nf, struct ssam_event_registry reg, · 398 unmodified lines
407407 return NULL;
408408 }
409409
410+/**
411+ * ssam_nf_refcount_dec_free() - Decrement reference-/activation-count of the
412+ * given event and free its entry if the reference count reaches zero.
413+ * @nf: The notifier system reference.
414+ * @reg: The registry used to enable/disable the event.
415+ * @id: The event ID.
416+ *
417+ * Decrements the reference-/activation-count of the specified event, freeing
418+ * its entry if it reaches zero.
419+ *
420+ * Note: ``nf->lock`` must be held when calling this function.
421+ */
422+static void ssam_nf_refcount_dec_free(struct ssam_nf *nf,
423+ struct ssam_event_registry reg,
424+ struct ssam_event_id id)
425+{
426+ struct ssam_nf_refcount_entry *entry;
427+
428+ lockdep_assert_held(&nf->lock);
429+
430+ entry = ssam_nf_refcount_dec(nf, reg, id);
431+ if (entry && entry->refcount == 0)
432+ kfree(entry);
433+}
434+
410435 /**
411436 * ssam_nf_refcount_empty() - Test if the notification system has any
412437 * enabled/active events.
@@ -2122,14 +2147,123 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl) · 1709 unmodified lines
21222147
21232148 /* -- Top-level event registry interface. ----------------------------------- */
21242149
2150+/**
2151+ * ssam_nf_refcount_enable() - Enable event for reference count entry if it has
2152+ * not already been enabled.
2153+ * @ctrl: The controller to enable the event on.
2154+ * @entry: The reference count entry for the event to be enabled.
2155+ * @flags: The flags used for enabling the event on the EC.
2156+ *
2157+ * Enable the event associated with the given reference count entry if the
2158+ * reference count equals one, i.e. the event has not previously been enabled.
2159+ * If the event has already been enabled (i.e. reference count not equal to
2160+ * one), check that the flags used for enabling match and warn about this if
2161+ * they do not.
2162+ *
2163+ * This does not modify the reference count itself, which is done with
2164+ * ssam_nf_refcount_inc() / ssam_nf_refcount_dec().
2165+ *
2166+ * Note: ``nf->lock`` must be held when calling this function.
2167+ *
2168+ * Return: Returns zero on success. If the event is enabled by this call,
2169+ * returns the status of the event-enable EC command.
2170+ */
2171+static int ssam_nf_refcount_enable(struct ssam_controller *ctrl,
2172+ struct ssam_nf_refcount_entry *entry, u8 flags)
2173+{
2174+ const struct ssam_event_registry reg = entry->key.reg;
2175+ const struct ssam_event_id id = entry->key.id;
2176+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
2177+ int status;
2178+
2179+ lockdep_assert_held(&nf->lock);
2180+
2181+ ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
2182+ reg.target_category, id.target_category, id.instance, entry->refcount);
2183+
2184+ if (entry->refcount == 1) {
2185+ status = ssam_ssh_event_enable(ctrl, reg, id, flags);
2186+ if (status)
2187+ return status;
2188+
2189+ entry->flags = flags;
2190+
2191+ } else if (entry->flags != flags) {
2192+ ssam_warn(ctrl,
2193+ "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
2194+ flags, entry->flags, reg.target_category, id.target_category,
2195+ id.instance);
2196+ }
2197+
2198+ return 0;
2199+}
2200+
2201+/**
2202+ * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is
2203+ * no longer in use and free the corresponding entry.
2204+ * @ctrl: The controller to disable the event on.
2205+ * @entry: The reference count entry for the event to be disabled.
2206+ * @flags: The flags used for enabling the event on the EC.
2207+ *
2208+ * If the reference count equals zero, i.e. the event is no longer requested by
2209+ * any client, the event will be disabled and the corresponding reference count
2210+ * entry freed. The reference count entry must not be used any more after a
2211+ * call to this function.
2212+ *
2213+ * Also checks if the flags used for disabling the event match the flags used
2214+ * for enabling the event and warns if they do not (regardless of reference
2215+ * count).
2216+ *
2217+ * This does not modify the reference count itself, which is done with
2218+ * ssam_nf_refcount_inc() / ssam_nf_refcount_dec().
2219+ *
2220+ * Note: ``nf->lock`` must be held when calling this function.
2221+ *
2222+ * Return: Returns zero on success. If the event is disabled by this call,
2223+ * returns the status of the event-enable EC command.
2224+ */
2225+static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl,
2226+ struct ssam_nf_refcount_entry *entry, u8 flags)
2227+{
2228+ const struct ssam_event_registry reg = entry->key.reg;
2229+ const struct ssam_event_id id = entry->key.id;
2230+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
2231+ int status = 0;
2232+
2233+ lockdep_assert_held(&nf->lock);
2234+
2235+ ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
2236+ reg.target_category, id.target_category, id.instance, entry->refcount);
2237+
2238+ if (entry->flags != flags) {
2239+ ssam_warn(ctrl,
2240+ "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
2241+ flags, entry->flags, reg.target_category, id.target_category,
2242+ id.instance);
2243+ }
2244+
2245+ if (entry->refcount == 0) {
2246+ status = ssam_ssh_event_disable(ctrl, reg, id, flags);
2247+ kfree(entry);
2248+ }
2249+
2250+ return status;
2251+}
2252+
21252253 /**
21262254 * ssam_notifier_register() - Register an event notifier.
21272255 * @ctrl: The controller to register the notifier on.
21282256 * @n: The event notifier to register.
21292257 *
2130− * Register an event notifier and increment the usage counter of the
2131− * associated SAM event. If the event was previously not enabled, it will be
2132− * enabled during this call.
2258+ * Register an event notifier. Increment the usage counter of the associated
2259+ * SAM event if the notifier is not marked as an observer. If the event is not
2260+ * marked as an observer and is currently not enabled, it will be enabled
2261+ * during this call. If the notifier is marked as an observer, no attempt will
2262+ * be made at enabling any event and no reference count will be modified.
2263+ *
2264+ * Notifiers marked as observers do not need to be associated with one specific
2265+ * event, i.e. as long as no event matching is performed, only the event target
2266+ * category needs to be set.
21332267 *
21342268 * Return: Returns zero on success, %-ENOSPC if there have already been
21352269 * %INT_MAX notifiers for the event ID/type associated with the notifier block
@@ -2138,11 +2272,10 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl) · 2 unmodified lines
21382272 * for the specific associated event, returns the status of the event-enable
21392273 * EC-command.
21402274 */
2141−int ssam_notifier_register(struct ssam_controller *ctrl,
2142− struct ssam_event_notifier *n)
2275+int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
21432276 {
21442277 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
2145− struct ssam_nf_refcount_entry *entry;
2278+ struct ssam_nf_refcount_entry *entry = NULL;
21462279 struct ssam_nf_head *nf_head;
21472280 struct ssam_nf *nf;
21482281 int status;
@@ -2155,44 +2288,32 @@ int ssam_notifier_register(struct ssam_controller *ctrl, · 6 unmodified lines
21552288
21562289 mutex_lock(&nf->lock);
21572290
2158− entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
2159− if (IS_ERR(entry)) {
2160− mutex_unlock(&nf->lock);
2161− return PTR_ERR(entry);
2291+ if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
2292+ entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
2293+ if (IS_ERR(entry)) {
2294+ mutex_unlock(&nf->lock);
2295+ return PTR_ERR(entry);
2296+ }
21622297 }
21632298
2164− ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
2165− n->event.reg.target_category, n->event.id.target_category,
2166− n->event.id.instance, entry->refcount);
2167−
21682299 status = ssam_nfblk_insert(nf_head, &n->base);
21692300 if (status) {
2170− entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
2171− if (entry->refcount == 0)
2172− kfree(entry);
2301+ if (entry)
2302+ ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
21732303
21742304 mutex_unlock(&nf->lock);
21752305 return status;
21762306 }
21772307
2178− if (entry->refcount == 1) {
2179− status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id,
2180− n->event.flags);
2308+ if (entry) {
2309+ status = ssam_nf_refcount_enable(ctrl, entry, n->event.flags);
21812310 if (status) {
21822311 ssam_nfblk_remove(&n->base);
2183− kfree(ssam_nf_refcount_dec(nf, n->event.reg, n->event.id));
2312+ ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
21842313 mutex_unlock(&nf->lock);
21852314 synchronize_srcu(&nf_head->srcu);
21862315 return status;
21872316 }
2188−
2189− entry->flags = n->event.flags;
2190−
2191− } else if (entry->flags != n->event.flags) {
2192− ssam_warn(ctrl,
2193− "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
2194− n->event.flags, entry->flags, n->event.reg.target_category,
2195− n->event.id.target_category, n->event.id.instance);
21962317 }
21972318
21982319 mutex_unlock(&nf->lock);
@@ -2205,17 +2326,16 @@ EXPORT_SYMBOL_GPL(ssam_notifier_register); · 6 unmodified lines
22052326 * @ctrl: The controller the notifier has been registered on.
22062327 * @n: The event notifier to unregister.
22072328 *
2208− * Unregister an event notifier and decrement the usage counter of the
2209− * associated SAM event. If the usage counter reaches zero, the event will be
2210− * disabled.
2329+ * Unregister an event notifier. Decrement the usage counter of the associated
2330+ * SAM event if the notifier is not marked as an observer. If the usage counter
2331+ * reaches zero, the event will be disabled.
22112332 *
22122333 * Return: Returns zero on success, %-ENOENT if the given notifier block has
22132334 * not been registered on the controller. If the given notifier block was the
22142335 * last one associated with its specific event, returns the status of the
22152336 * event-disable EC-command.
22162337 */
2217−int ssam_notifier_unregister(struct ssam_controller *ctrl,
2218− struct ssam_event_notifier *n)
2338+int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
22192339 {
22202340 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
22212341 struct ssam_nf_refcount_entry *entry;
@@ -2236,33 +2356,24 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, · 14 unmodified lines
22362356 return -ENOENT;
22372357 }
22382358
2239− entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
2240− if (WARN_ON(!entry)) {
2241− /*
2242− * If this does not return an entry, there's a logic error
2243− * somewhere: The notifier block is registered, but the event
2244− * refcount entry is not there. Remove the notifier block
2245− * anyways.
2246− */
2247− status = -ENOENT;
2248− goto remove;
2249− }
2250−
2251− ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",
2252− n->event.reg.target_category, n->event.id.target_category,
2253− n->event.id.instance, entry->refcount);
2254−
2255− if (entry->flags != n->event.flags) {
2256− ssam_warn(ctrl,
2257− "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n",
2258− n->event.flags, entry->flags, n->event.reg.target_category,
2259− n->event.id.target_category, n->event.id.instance);
2260− }
2359+ /*
2360+ * If this is an observer notifier, do not attempt to disable the
2361+ * event, just remove it.
2362+ */
2363+ if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
2364+ entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
2365+ if (WARN_ON(!entry)) {
2366+ /*
2367+ * If this does not return an entry, there's a logic
2368+ * error somewhere: The notifier block is registered,
2369+ * but the event refcount entry is not there. Remove
2370+ * the notifier block anyways.
2371+ */
2372+ status = -ENOENT;
2373+ goto remove;
2374+ }
22612375
2262− if (entry->refcount == 0) {
2263− status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id,
2264− n->event.flags);
2265− kfree(entry);
2376+ status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags);
22662377 }
22672378
22682379 remove:
@@ -2274,6 +2385,105 @@ remove: · 5 unmodified lines
22742385 }
22752386 EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
22762387
2388+/**
2389+ * ssam_controller_event_enable() - Enable the specified event.
2390+ * @ctrl: The controller to enable the event for.
2391+ * @reg: The event registry to use for enabling the event.
2392+ * @id: The event ID specifying the event to be enabled.
2393+ * @flags: The SAM event flags used for enabling the event.
2394+ *
2395+ * Increment the event reference count of the specified event. If the event has
2396+ * not been enabled previously, it will be enabled by this call.
2397+ *
2398+ * Note: In general, ssam_notifier_register() with a non-observer notifier
2399+ * should be preferred for enabling/disabling events, as this will guarantee
2400+ * proper ordering and event forwarding in case of errors during event
2401+ * enabling/disabling.
2402+ *
2403+ * Return: Returns zero on success, %-ENOSPC if the reference count for the
2404+ * specified event has reached its maximum, %-ENOMEM if the corresponding event
2405+ * entry could not be allocated. If this is the first time that this event has
2406+ * been enabled (i.e. the reference count was incremented from zero to one by
2407+ * this call), returns the status of the event-enable EC-command.
2408+ */
2409+int ssam_controller_event_enable(struct ssam_controller *ctrl,
2410+ struct ssam_event_registry reg,
2411+ struct ssam_event_id id, u8 flags)
2412+{
2413+ u16 rqid = ssh_tc_to_rqid(id.target_category);
2414+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
2415+ struct ssam_nf_refcount_entry *entry;
2416+ int status;
2417+
2418+ if (!ssh_rqid_is_event(rqid))
2419+ return -EINVAL;
2420+
2421+ mutex_lock(&nf->lock);
2422+
2423+ entry = ssam_nf_refcount_inc(nf, reg, id);
2424+ if (IS_ERR(entry)) {
2425+ mutex_unlock(&nf->lock);
2426+ return PTR_ERR(entry);
2427+ }
2428+
2429+ status = ssam_nf_refcount_enable(ctrl, entry, flags);
2430+ if (status) {
2431+ ssam_nf_refcount_dec_free(nf, reg, id);
2432+ mutex_unlock(&nf->lock);
2433+ return status;
2434+ }
2435+
2436+ mutex_unlock(&nf->lock);
2437+ return 0;
2438+}
2439+EXPORT_SYMBOL_GPL(ssam_controller_event_enable);
2440+
2441+/**
2442+ * ssam_controller_event_disable() - Disable the specified event.
2443+ * @ctrl: The controller to disable the event for.
2444+ * @reg: The event registry to use for disabling the event.
2445+ * @id: The event ID specifying the event to be disabled.
2446+ * @flags: The flags used when enabling the event.
2447+ *
2448+ * Decrement the reference count of the specified event. If the reference count
2449+ * reaches zero, the event will be disabled.
2450+ *
2451+ * Note: In general, ssam_notifier_register()/ssam_notifier_unregister() with a
2452+ * non-observer notifier should be preferred for enabling/disabling events, as
2453+ * this will guarantee proper ordering and event forwarding in case of errors
2454+ * during event enabling/disabling.
2455+ *
2456+ * Return: Returns zero on success, %-ENOENT if the given event has not been
2457+ * enabled on the controller. If the reference count of the event reaches zero
2458+ * during this call, returns the status of the event-disable EC-command.
2459+ */
2460+int ssam_controller_event_disable(struct ssam_controller *ctrl,
2461+ struct ssam_event_registry reg,
2462+ struct ssam_event_id id, u8 flags)
2463+{
2464+ u16 rqid = ssh_tc_to_rqid(id.target_category);
2465+ struct ssam_nf *nf = &ctrl->cplt.event.notif;
2466+ struct ssam_nf_refcount_entry *entry;
2467+ int status;
2468+
2469+ if (!ssh_rqid_is_event(rqid))
2470+ return -EINVAL;
2471+
2472+ mutex_lock(&nf->lock);
2473+
2474+ entry = ssam_nf_refcount_dec(nf, reg, id);
2475+ if (!entry) {
2476+ mutex_unlock(&nf->lock);
2477+ return -ENOENT;
2478+ }
2479+
2480+ status = ssam_nf_refcount_disable_free(ctrl, entry, flags);
2481+
2482+ mutex_unlock(&nf->lock);
2483+ return status;
2484+}
2485+EXPORT_SYMBOL_GPL(ssam_controller_event_disable);
2486+
22772487 /**
22782488 * ssam_notifier_disable_registered() - Disable events for all registered
22792489 * notifiers.
drivers/platform/surface/aggregator/controller.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * Main SSAM/SSH controller structure and functionality.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #ifndef _SURFACE_AGGREGATOR_CONTROLLER_H
drivers/platform/surface/aggregator/core.c+1−1
@@ -7,7 +7,7 @@ · 6 unmodified lines
77 * Handles communication via requests as well as enabling, disabling, and
88 * relaying of events.
99 *
10− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
10+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
1111 */
1212
1313 #include <linux/acpi.h>
drivers/platform/surface/aggregator/ssh_msgb.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH message builder functions.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H
drivers/platform/surface/aggregator/ssh_packet_layer.c+4−8
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH packet transport layer.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #include <asm/unaligned.h>
@@ -1567,9 +1567,7 @@ static void ssh_ptl_timeout_reap(struct work_struct *work) · 1558 unmodified lines
15671567 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);
15681568
15691569 atomic_dec(&ptl->pending.count);
1570− list_del(&p->pending_node);
1571−
1572− list_add_tail(&p->pending_node, &claimed);
1570+ list_move_tail(&p->pending_node, &claimed);
15731571 }
15741572
15751573 spin_unlock(&ptl->pending.lock);
@@ -1957,8 +1955,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl) · 381 unmodified lines
19571955 smp_mb__before_atomic();
19581956 clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state);
19591957
1960− list_del(&p->queue_node);
1961− list_add_tail(&p->queue_node, &complete_q);
1958+ list_move_tail(&p->queue_node, &complete_q);
19621959 }
19631960 spin_unlock(&ptl->queue.lock);
19641961
@@ -1970,8 +1967,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl) · 5 unmodified lines
19701967 smp_mb__before_atomic();
19711968 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);
19721969
1973− list_del(&p->pending_node);
1974− list_add_tail(&p->pending_node, &complete_q);
1970+ list_move_tail(&p->pending_node, &complete_q);
19751971 }
19761972 atomic_set(&ptl->pending.count, 0);
19771973 spin_unlock(&ptl->pending.lock);
drivers/platform/surface/aggregator/ssh_packet_layer.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH packet transport layer.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H
drivers/platform/surface/aggregator/ssh_parser.c+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH message parser.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #include <asm/unaligned.h>
drivers/platform/surface/aggregator/ssh_parser.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH message parser.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H
drivers/platform/surface/aggregator/ssh_request_layer.c+4−8
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH request transport layer.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #include <asm/unaligned.h>
@@ -863,9 +863,7 @@ static void ssh_rtl_timeout_reap(struct work_struct *work) · 854 unmodified lines
863863 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);
864864
865865 atomic_dec(&rtl->pending.count);
866− list_del(&r->node);
867−
868− list_add_tail(&r->node, &claimed);
866+ list_move_tail(&r->node, &claimed);
869867 }
870868 spin_unlock(&rtl->pending.lock);
871869
@@ -1204,8 +1202,7 @@ void ssh_rtl_shutdown(struct ssh_rtl *rtl) · 332 unmodified lines
12041202 smp_mb__before_atomic();
12051203 clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state);
12061204
1207− list_del(&r->node);
1208− list_add_tail(&r->node, &claimed);
1205+ list_move_tail(&r->node, &claimed);
12091206 }
12101207 spin_unlock(&rtl->queue.lock);
12111208
@@ -1238,8 +1235,7 @@ void ssh_rtl_shutdown(struct ssh_rtl *rtl) · 26 unmodified lines
12381235 smp_mb__before_atomic();
12391236 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);
12401237
1241− list_del(&r->node);
1242− list_add_tail(&r->node, &claimed);
1238+ list_move_tail(&r->node, &claimed);
12431239 }
12441240 spin_unlock(&rtl->pending.lock);
12451241 }
drivers/platform/surface/aggregator/ssh_request_layer.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * SSH request transport layer.
44 *
5− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H
drivers/platform/surface/aggregator/trace.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 /*
33 * Trace points for SSAM/SSH.
44 *
5− * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
5+ * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
66 */
77
88 #undef TRACE_SYSTEM
drivers/platform/surface/surface_aggregator_cdev.c+511−23
@@ -3,29 +3,69 @@ · 2 unmodified lines
33 * Provides user-space access to the SSAM EC via the /dev/surface/aggregator
44 * misc device. Intended for debugging and development.
55 *
6− * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
6+ * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
77 */
88
99 #include <linux/fs.h>
10+#include <linux/ioctl.h>
1011 #include <linux/kernel.h>
12+#include <linux/kfifo.h>
1113 #include <linux/kref.h>
1214 #include <linux/miscdevice.h>
1315 #include <linux/module.h>
1416 #include <linux/platform_device.h>
17+#include <linux/poll.h>
1518 #include <linux/rwsem.h>
1619 #include <linux/slab.h>
1720 #include <linux/uaccess.h>
21+#include <linux/vmalloc.h>
1822
1923 #include <linux/surface_aggregator/cdev.h>
2024 #include <linux/surface_aggregator/controller.h>
25+#include <linux/surface_aggregator/serial_hub.h>
2126
2227 #define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev"
2328
29+
30+/* -- Main structures. ------------------------------------------------------ */
31+
32+enum ssam_cdev_device_state {
33+ SSAM_CDEV_DEVICE_SHUTDOWN_BIT = BIT(0),
34+};
35+
2436 struct ssam_cdev {
2537 struct kref kref;
2638 struct rw_semaphore lock;
39+
40+ struct device *dev;
2741 struct ssam_controller *ctrl;
2842 struct miscdevice mdev;
43+ unsigned long flags;
44+
45+ struct rw_semaphore client_lock; /* Guards client list. */
46+ struct list_head client_list;
47+};
48+
49+struct ssam_cdev_client;
50+
51+struct ssam_cdev_notifier {
52+ struct ssam_cdev_client *client;
53+ struct ssam_event_notifier nf;
54+};
55+
56+struct ssam_cdev_client {
57+ struct ssam_cdev *cdev;
58+ struct list_head node;
59+
60+ struct mutex notifier_lock; /* Guards notifier access for registration */
61+ struct ssam_cdev_notifier *notifier[SSH_NUM_EVENTS];
62+
63+ struct mutex read_lock; /* Guards FIFO buffer read access */
64+ struct mutex write_lock; /* Guards FIFO buffer write access */
65+ DECLARE_KFIFO(buffer, u8, 4096);
66+
67+ wait_queue_head_t waitq;
68+ struct fasync_struct *fasync;
2969 };
3070
3171 static void __ssam_cdev_release(struct kref *kref)
@@ -47,24 +87,173 @@ static void ssam_cdev_put(struct ssam_cdev *cdev) · 15 unmodified lines
4787 kref_put(&cdev->kref, __ssam_cdev_release);
4888 }
4989
50−static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
90+
91+/* -- Notifier handling. ---------------------------------------------------- */
92+
93+static u32 ssam_cdev_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in)
5194 {
52− struct miscdevice *mdev = filp->private_data;
53− struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
95+ struct ssam_cdev_notifier *cdev_nf = container_of(nf, struct ssam_cdev_notifier, nf);
96+ struct ssam_cdev_client *client = cdev_nf->client;
97+ struct ssam_cdev_event event;
98+ size_t n = struct_size(&event, data, in->length);
99+
100+ /* Translate event. */
101+ event.target_category = in->target_category;
102+ event.target_id = in->target_id;
103+ event.command_id = in->command_id;
104+ event.instance_id = in->instance_id;
105+ event.length = in->length;
106+
107+ mutex_lock(&client->write_lock);
108+
109+ /* Make sure we have enough space. */
110+ if (kfifo_avail(&client->buffer) < n) {
111+ dev_warn(client->cdev->dev,
112+ "buffer full, dropping event (tc: %#04x, tid: %#04x, cid: %#04x, iid: %#04x)\n",
113+ in->target_category, in->target_id, in->command_id, in->instance_id);
114+ mutex_unlock(&client->write_lock);
115+ return 0;
116+ }
117+
118+ /* Copy event header and payload. */
119+ kfifo_in(&client->buffer, (const u8 *)&event, struct_size(&event, data, 0));
120+ kfifo_in(&client->buffer, &in->data[0], in->length);
121+
122+ mutex_unlock(&client->write_lock);
123+
124+ /* Notify waiting readers. */
125+ kill_fasync(&client->fasync, SIGIO, POLL_IN);
126+ wake_up_interruptible(&client->waitq);
54127
55− filp->private_data = ssam_cdev_get(cdev);
56− return stream_open(inode, filp);
128+ /*
129+ * Don't mark events as handled, this is the job of a proper driver and
130+ * not the debugging interface.
131+ */
132+ return 0;
57133 }
58134
59−static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
135+static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, int priority)
60136 {
61− ssam_cdev_put(filp->private_data);
62− return 0;
137+ const u16 rqid = ssh_tc_to_rqid(tc);
138+ const u16 event = ssh_rqid_to_event(rqid);
139+ struct ssam_cdev_notifier *nf;
140+ int status;
141+
142+ lockdep_assert_held_read(&client->cdev->lock);
143+
144+ /* Validate notifier target category. */
145+ if (!ssh_rqid_is_event(rqid))
146+ return -EINVAL;
147+
148+ mutex_lock(&client->notifier_lock);
149+
150+ /* Check if the notifier has already been registered. */
151+ if (client->notifier[event]) {
152+ mutex_unlock(&client->notifier_lock);
153+ return -EEXIST;
154+ }
155+
156+ /* Allocate new notifier. */
157+ nf = kzalloc(sizeof(*nf), GFP_KERNEL);
158+ if (!nf) {
159+ mutex_unlock(&client->notifier_lock);
160+ return -ENOMEM;
161+ }
162+
163+ /*
164+ * Create a dummy notifier with the minimal required fields for
165+ * observer registration. Note that we can skip fully specifying event
166+ * and registry here as we do not need any matching and use silent
167+ * registration, which does not enable the corresponding event.
168+ */
169+ nf->client = client;
170+ nf->nf.base.fn = ssam_cdev_notifier;
171+ nf->nf.base.priority = priority;
172+ nf->nf.event.id.target_category = tc;
173+ nf->nf.event.mask = 0; /* Do not do any matching. */
174+ nf->nf.flags = SSAM_EVENT_NOTIFIER_OBSERVER;
175+
176+ /* Register notifier. */
177+ status = ssam_notifier_register(client->cdev->ctrl, &nf->nf);
178+ if (status)
179+ kfree(nf);
180+ else
181+ client->notifier[event] = nf;
182+
183+ mutex_unlock(&client->notifier_lock);
184+ return status;
185+}
186+
187+static int ssam_cdev_notifier_unregister(struct ssam_cdev_client *client, u8 tc)
188+{
189+ const u16 rqid = ssh_tc_to_rqid(tc);
190+ const u16 event = ssh_rqid_to_event(rqid);
191+ int status;
192+
193+ lockdep_assert_held_read(&client->cdev->lock);
194+
195+ /* Validate notifier target category. */
196+ if (!ssh_rqid_is_event(rqid))
197+ return -EINVAL;
198+
199+ mutex_lock(&client->notifier_lock);
200+
201+ /* Check if the notifier is currently registered. */
202+ if (!client->notifier[event]) {
203+ mutex_unlock(&client->notifier_lock);
204+ return -ENOENT;
205+ }
206+
207+ /* Unregister and free notifier. */
208+ status = ssam_notifier_unregister(client->cdev->ctrl, &client->notifier[event]->nf);
209+ kfree(client->notifier[event]);
210+ client->notifier[event] = NULL;
211+
212+ mutex_unlock(&client->notifier_lock);
213+ return status;
214+}
215+
216+static void ssam_cdev_notifier_unregister_all(struct ssam_cdev_client *client)
217+{
218+ int i;
219+
220+ down_read(&client->cdev->lock);
221+
222+ /*
223+ * This function may be used during shutdown, thus we need to test for
224+ * cdev->ctrl instead of the SSAM_CDEV_DEVICE_SHUTDOWN_BIT bit.
225+ */
226+ if (client->cdev->ctrl) {
227+ for (i = 0; i < SSH_NUM_EVENTS; i++)
228+ ssam_cdev_notifier_unregister(client, i + 1);
229+
230+ } else {
231+ int count = 0;
232+
233+ /*
234+ * Device has been shut down. Any notifier remaining is a bug,
235+ * so warn about that as this would otherwise hardly be
236+ * noticeable. Nevertheless, free them as well.
237+ */
238+ mutex_lock(&client->notifier_lock);
239+ for (i = 0; i < SSH_NUM_EVENTS; i++) {
240+ count += !!(client->notifier[i]);
241+ kfree(client->notifier[i]);
242+ client->notifier[i] = NULL;
243+ }
244+ mutex_unlock(&client->notifier_lock);
245+
246+ WARN_ON(count > 0);
247+ }
248+
249+ up_read(&client->cdev->lock);
63250 }
64251
65−static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)
252+
253+/* -- IOCTL functions. ------------------------------------------------------ */
254+
255+static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_request __user *r)
66256 {
67− struct ssam_cdev_request __user *r;
68257 struct ssam_cdev_request rqst;
69258 struct ssam_request spec = {};
70259 struct ssam_response rsp = {};
@@ -72,7 +261,8 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg) · 1 unmodified line
72261 void __user *rspdata;
73262 int status = 0, ret = 0, tmp;
74263
75− r = (struct ssam_cdev_request __user *)arg;
264+ lockdep_assert_held_read(&client->cdev->lock);
265+
76266 ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r));
77267 if (ret)
78268 goto out;
@@ -152,7 +342,7 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg) · 73 unmodified lines
152342 }
153343
154344 /* Perform request. */
155− status = ssam_request_sync(cdev->ctrl, &spec, &rsp);
345+ status = ssam_request_sync(client->cdev->ctrl, &spec, &rsp);
156346 if (status)
157347 goto out;
158348
@@ -177,48 +367,315 @@ out: · 18 unmodified lines
177367 return ret;
178368 }
179369
180−static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd,
370+static long ssam_cdev_notif_register(struct ssam_cdev_client *client,
371+ const struct ssam_cdev_notifier_desc __user *d)
372+{
373+ struct ssam_cdev_notifier_desc desc;
374+ long ret;
375+
376+ lockdep_assert_held_read(&client->cdev->lock);
377+
378+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
379+ if (ret)
380+ return ret;
381+
382+ return ssam_cdev_notifier_register(client, desc.target_category, desc.priority);
383+}
384+
385+static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client,
386+ const struct ssam_cdev_notifier_desc __user *d)
387+{
388+ struct ssam_cdev_notifier_desc desc;
389+ long ret;
390+
391+ lockdep_assert_held_read(&client->cdev->lock);
392+
393+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
394+ if (ret)
395+ return ret;
396+
397+ return ssam_cdev_notifier_unregister(client, desc.target_category);
398+}
399+
400+static long ssam_cdev_event_enable(struct ssam_cdev_client *client,
401+ const struct ssam_cdev_event_desc __user *d)
402+{
403+ struct ssam_cdev_event_desc desc;
404+ struct ssam_event_registry reg;
405+ struct ssam_event_id id;
406+ long ret;
407+
408+ lockdep_assert_held_read(&client->cdev->lock);
409+
410+ /* Read descriptor from user-space. */
411+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
412+ if (ret)
413+ return ret;
414+
415+ /* Translate descriptor. */
416+ reg.target_category = desc.reg.target_category;
417+ reg.target_id = desc.reg.target_id;
418+ reg.cid_enable = desc.reg.cid_enable;
419+ reg.cid_disable = desc.reg.cid_disable;
420+
421+ id.target_category = desc.id.target_category;
422+ id.instance = desc.id.instance;
423+
424+ /* Disable event. */
425+ return ssam_controller_event_enable(client->cdev->ctrl, reg, id, desc.flags);
426+}
427+
428+static long ssam_cdev_event_disable(struct ssam_cdev_client *client,
429+ const struct ssam_cdev_event_desc __user *d)
430+{
431+ struct ssam_cdev_event_desc desc;
432+ struct ssam_event_registry reg;
433+ struct ssam_event_id id;
434+ long ret;
435+
436+ lockdep_assert_held_read(&client->cdev->lock);
437+
438+ /* Read descriptor from user-space. */
439+ ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d));
440+ if (ret)
441+ return ret;
442+
443+ /* Translate descriptor. */
444+ reg.target_category = desc.reg.target_category;
445+ reg.target_id = desc.reg.target_id;
446+ reg.cid_enable = desc.reg.cid_enable;
447+ reg.cid_disable = desc.reg.cid_disable;
448+
449+ id.target_category = desc.id.target_category;
450+ id.instance = desc.id.instance;
451+
452+ /* Disable event. */
453+ return ssam_controller_event_disable(client->cdev->ctrl, reg, id, desc.flags);
454+}
455+
456+
457+/* -- File operations. ------------------------------------------------------ */
458+
459+static int ssam_cdev_device_open(struct inode *inode, struct file *filp)
460+{
461+ struct miscdevice *mdev = filp->private_data;
462+ struct ssam_cdev_client *client;
463+ struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);
464+
465+ /* Initialize client */
466+ client = vzalloc(sizeof(*client));
467+ if (!client)
468+ return -ENOMEM;
469+
470+ client->cdev = ssam_cdev_get(cdev);
471+
472+ INIT_LIST_HEAD(&client->node);
473+
474+ mutex_init(&client->notifier_lock);
475+
476+ mutex_init(&client->read_lock);
477+ mutex_init(&client->write_lock);
478+ INIT_KFIFO(client->buffer);
479+ init_waitqueue_head(&client->waitq);
480+
481+ filp->private_data = client;
482+
483+ /* Attach client. */
484+ down_write(&cdev->client_lock);
485+
486+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
487+ up_write(&cdev->client_lock);
488+ mutex_destroy(&client->write_lock);
489+ mutex_destroy(&client->read_lock);
490+ mutex_destroy(&client->notifier_lock);
491+ ssam_cdev_put(client->cdev);
492+ vfree(client);
493+ return -ENODEV;
494+ }
495+ list_add_tail(&client->node, &cdev->client_list);
496+
497+ up_write(&cdev->client_lock);
498+
499+ stream_open(inode, filp);
500+ return 0;
501+}
502+
503+static int ssam_cdev_device_release(struct inode *inode, struct file *filp)
504+{
505+ struct ssam_cdev_client *client = filp->private_data;
506+
507+ /* Force-unregister all remaining notifiers of this client. */
508+ ssam_cdev_notifier_unregister_all(client);
509+
510+ /* Detach client. */
511+ down_write(&client->cdev->client_lock);
512+ list_del(&client->node);
513+ up_write(&client->cdev->client_lock);
514+
515+ /* Free client. */
516+ mutex_destroy(&client->write_lock);
517+ mutex_destroy(&client->read_lock);
518+
519+ mutex_destroy(&client->notifier_lock);
520+
521+ ssam_cdev_put(client->cdev);
522+ vfree(client);
523+
524+ return 0;
525+}
526+
527+static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd,
181528 unsigned long arg)
182529 {
530+ lockdep_assert_held_read(&client->cdev->lock);
531+
183532 switch (cmd) {
184533 case SSAM_CDEV_REQUEST:
185− return ssam_cdev_request(cdev, arg);
534+ return ssam_cdev_request(client, (struct ssam_cdev_request __user *)arg);
535+
536+ case SSAM_CDEV_NOTIF_REGISTER:
537+ return ssam_cdev_notif_register(client,
538+ (struct ssam_cdev_notifier_desc __user *)arg);
539+
540+ case SSAM_CDEV_NOTIF_UNREGISTER:
541+ return ssam_cdev_notif_unregister(client,
542+ (struct ssam_cdev_notifier_desc __user *)arg);
543+
544+ case SSAM_CDEV_EVENT_ENABLE:
545+ return ssam_cdev_event_enable(client, (struct ssam_cdev_event_desc __user *)arg);
546+
547+ case SSAM_CDEV_EVENT_DISABLE:
548+ return ssam_cdev_event_disable(client, (struct ssam_cdev_event_desc __user *)arg);
186549
187550 default:
188551 return -ENOTTY;
189552 }
190553 }
191554
192−static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd,
193− unsigned long arg)
555+static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
194556 {
195− struct ssam_cdev *cdev = file->private_data;
557+ struct ssam_cdev_client *client = file->private_data;
196558 long status;
197559
198560 /* Ensure that controller is valid for as long as we need it. */
561+ if (down_read_killable(&client->cdev->lock))
562+ return -ERESTARTSYS;
563+
564+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) {
565+ up_read(&client->cdev->lock);
566+ return -ENODEV;
567+ }
568+
569+ status = __ssam_cdev_device_ioctl(client, cmd, arg);
570+
571+ up_read(&client->cdev->lock);
572+ return status;
573+}
574+
575+static ssize_t ssam_cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offs)
576+{
577+ struct ssam_cdev_client *client = file->private_data;
578+ struct ssam_cdev *cdev = client->cdev;
579+ unsigned int copied;
580+ int status = 0;
581+
199582 if (down_read_killable(&cdev->lock))
200583 return -ERESTARTSYS;
201584
202− if (!cdev->ctrl) {
585+ /* Make sure we're not shut down. */
586+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
203587 up_read(&cdev->lock);
204588 return -ENODEV;
205589 }
206590
207− status = __ssam_cdev_device_ioctl(cdev, cmd, arg);
591+ do {
592+ /* Check availability, wait if necessary. */
593+ if (kfifo_is_empty(&client->buffer)) {
594+ up_read(&cdev->lock);
595+
596+ if (file->f_flags & O_NONBLOCK)
597+ return -EAGAIN;
598+
599+ status = wait_event_interruptible(client->waitq,
600+ !kfifo_is_empty(&client->buffer) ||
601+ test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT,
602+ &cdev->flags));
603+ if (status < 0)
604+ return status;
605+
606+ if (down_read_killable(&cdev->lock))
607+ return -ERESTARTSYS;
608+
609+ /* Need to check that we're not shut down again. */
610+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
611+ up_read(&cdev->lock);
612+ return -ENODEV;
613+ }
614+ }
615+
616+ /* Try to read from FIFO. */
617+ if (mutex_lock_interruptible(&client->read_lock)) {
618+ up_read(&cdev->lock);
619+ return -ERESTARTSYS;
620+ }
621+
622+ status = kfifo_to_user(&client->buffer, buf, count, &copied);
623+ mutex_unlock(&client->read_lock);
624+
625+ if (status < 0) {
626+ up_read(&cdev->lock);
627+ return status;
628+ }
629+
630+ /* We might not have gotten anything, check this here. */
631+ if (copied == 0 && (file->f_flags & O_NONBLOCK)) {
632+ up_read(&cdev->lock);
633+ return -EAGAIN;
634+ }
635+ } while (copied == 0);
208636
209637 up_read(&cdev->lock);
210− return status;
638+ return copied;
639+}
640+
641+static __poll_t ssam_cdev_poll(struct file *file, struct poll_table_struct *pt)
642+{
643+ struct ssam_cdev_client *client = file->private_data;
644+ __poll_t events = 0;
645+
646+ if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags))
647+ return EPOLLHUP | EPOLLERR;
648+
649+ poll_wait(file, &client->waitq, pt);
650+
651+ if (!kfifo_is_empty(&client->buffer))
652+ events |= EPOLLIN | EPOLLRDNORM;
653+
654+ return events;
655+}
656+
657+static int ssam_cdev_fasync(int fd, struct file *file, int on)
658+{
659+ struct ssam_cdev_client *client = file->private_data;
660+
661+ return fasync_helper(fd, file, on, &client->fasync);
211662 }
212663
213664 static const struct file_operations ssam_controller_fops = {
214665 .owner = THIS_MODULE,
215666 .open = ssam_cdev_device_open,
216667 .release = ssam_cdev_device_release,
668+ .read = ssam_cdev_read,
669+ .poll = ssam_cdev_poll,
670+ .fasync = ssam_cdev_fasync,
217671 .unlocked_ioctl = ssam_cdev_device_ioctl,
218672 .compat_ioctl = ssam_cdev_device_ioctl,
219− .llseek = noop_llseek,
673+ .llseek = no_llseek,
220674 };
221675
676+
677+/* -- Device and driver setup ----------------------------------------------- */
678+
222679 static int ssam_dbg_device_probe(struct platform_device *pdev)
223680 {
224681 struct ssam_controller *ctrl;
@@ -236,6 +693,7 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) · 11 unmodified lines
236693 kref_init(&cdev->kref);
237694 init_rwsem(&cdev->lock);
238695 cdev->ctrl = ctrl;
696+ cdev->dev = &pdev->dev;
239697
240698 cdev->mdev.parent = &pdev->dev;
241699 cdev->mdev.minor = MISC_DYNAMIC_MINOR;
@@ -243,6 +701,9 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) · 1 unmodified line
243701 cdev->mdev.nodename = "surface/aggregator";
244702 cdev->mdev.fops = &ssam_controller_fops;
245703
704+ init_rwsem(&cdev->client_lock);
705+ INIT_LIST_HEAD(&cdev->client_list);
706+
246707 status = misc_register(&cdev->mdev);
247708 if (status) {
248709 kfree(cdev);
@@ -256,8 +717,32 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) · 7 unmodified lines
256717 static int ssam_dbg_device_remove(struct platform_device *pdev)
257718 {
258719 struct ssam_cdev *cdev = platform_get_drvdata(pdev);
720+ struct ssam_cdev_client *client;
259721
260− misc_deregister(&cdev->mdev);
722+ /*
723+ * Mark device as shut-down. Prevent new clients from being added and
724+ * new operations from being executed.
725+ */
726+ set_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags);
727+
728+ down_write(&cdev->client_lock);
729+
730+ /* Remove all notifiers registered by us. */
731+ list_for_each_entry(client, &cdev->client_list, node) {
732+ ssam_cdev_notifier_unregister_all(client);
733+ }
734+
735+ /* Wake up async clients. */
736+ list_for_each_entry(client, &cdev->client_list, node) {
737+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
738+ }
739+
740+ /* Wake up blocking clients. */
741+ list_for_each_entry(client, &cdev->client_list, node) {
742+ wake_up_interruptible(&client->waitq);
743+ }
744+
745+ up_write(&cdev->client_lock);
261746
262747 /*
263748 * The controller is only guaranteed to be valid for as long as the
@@ -266,8 +751,11 @@ static int ssam_dbg_device_remove(struct platform_device *pdev) · 2 unmodified lines
266751 */
267752 down_write(&cdev->lock);
268753 cdev->ctrl = NULL;
754+ cdev->dev = NULL;
269755 up_write(&cdev->lock);
270756
757+ misc_deregister(&cdev->mdev);
758+
271759 ssam_cdev_put(cdev);
272760 return 0;
273761 }
drivers/platform/surface/surface_aggregator_registry.c+12−35
@@ -119,8 +119,13 @@ static const struct software_node ssam_node_hid_base_iid6 = { · 118 unmodified lines
119119 .parent = &ssam_node_hub_base,
120120 };
121121
122−/* Devices for Surface Book 2. */
123−static const struct software_node *ssam_node_group_sb2[] = {
122+/*
123+ * Devices for 5th- and 6th-generations models:
124+ * - Surface Book 2,
125+ * - Surface Laptop 1 and 2,
126+ * - Surface Pro 5 and 6.
127+ */
128+static const struct software_node *ssam_node_group_gen5[] = {
124129 &ssam_node_root,
125130 &ssam_node_tmp_pprof,
126131 NULL,
@@ -142,20 +147,6 @@ static const struct software_node *ssam_node_group_sb3[] = { · 15 unmodified lines
142147 NULL,
143148 };
144149
145−/* Devices for Surface Laptop 1. */
146−static const struct software_node *ssam_node_group_sl1[] = {
147− &ssam_node_root,
148− &ssam_node_tmp_pprof,
149− NULL,
150−};
151−
152−/* Devices for Surface Laptop 2. */
153−static const struct software_node *ssam_node_group_sl2[] = {
154− &ssam_node_root,
155− &ssam_node_tmp_pprof,
156− NULL,
157−};
158−
159150 /* Devices for Surface Laptop 3 and 4. */
160151 static const struct software_node *ssam_node_group_sl3[] = {
161152 &ssam_node_root,
@@ -177,20 +168,6 @@ static const struct software_node *ssam_node_group_slg1[] = { · 15 unmodified lines
177168 NULL,
178169 };
179170
180−/* Devices for Surface Pro 5. */
181−static const struct software_node *ssam_node_group_sp5[] = {
182− &ssam_node_root,
183− &ssam_node_tmp_pprof,
184− NULL,
185−};
186−
187−/* Devices for Surface Pro 6. */
188−static const struct software_node *ssam_node_group_sp6[] = {
189− &ssam_node_root,
190− &ssam_node_tmp_pprof,
191− NULL,
192−};
193−
194171 /* Devices for Surface Pro 7 and Surface Pro 7+. */
195172 static const struct software_node *ssam_node_group_sp7[] = {
196173 &ssam_node_root,
@@ -495,10 +472,10 @@ static struct ssam_device_driver ssam_base_hub_driver = { · 298 unmodified lines
495472
496473 static const struct acpi_device_id ssam_platform_hub_match[] = {
497474 /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
498− { "MSHW0081", (unsigned long)ssam_node_group_sp5 },
475+ { "MSHW0081", (unsigned long)ssam_node_group_gen5 },
499476
500477 /* Surface Pro 6 (OMBR >= 0x10) */
501− { "MSHW0111", (unsigned long)ssam_node_group_sp6 },
478+ { "MSHW0111", (unsigned long)ssam_node_group_gen5 },
502479
503480 /* Surface Pro 7 */
504481 { "MSHW0116", (unsigned long)ssam_node_group_sp7 },
@@ -507,16 +484,16 @@ static const struct acpi_device_id ssam_platform_hub_match[] = { · 2 unmodified lines
507484 { "MSHW0119", (unsigned long)ssam_node_group_sp7 },
508485
509486 /* Surface Book 2 */
510− { "MSHW0107", (unsigned long)ssam_node_group_sb2 },
487+ { "MSHW0107", (unsigned long)ssam_node_group_gen5 },
511488
512489 /* Surface Book 3 */
513490 { "MSHW0117", (unsigned long)ssam_node_group_sb3 },
514491
515492 /* Surface Laptop 1 */
516− { "MSHW0086", (unsigned long)ssam_node_group_sl1 },
493+ { "MSHW0086", (unsigned long)ssam_node_group_gen5 },
517494
518495 /* Surface Laptop 2 */
519− { "MSHW0112", (unsigned long)ssam_node_group_sl2 },
496+ { "MSHW0112", (unsigned long)ssam_node_group_gen5 },
520497
521498 /* Surface Laptop 3 (13", Intel) */
522499 { "MSHW0114", (unsigned long)ssam_node_group_sl3 },
drivers/platform/x86/Kconfig+21−28
@@ -415,16 +415,17 @@ config HP_ACCEL · 414 unmodified lines
415415 To compile this driver as a module, choose M here: the module will
416416 be called hp_accel.
417417
418−config HP_WIRELESS
419− tristate "HP wireless button"
418+config WIRELESS_HOTKEY
419+ tristate "Wireless hotkey button"
420420 depends on ACPI
421421 depends on INPUT
422422 help
423− This driver provides supports for new HP wireless button for Windows 8.
423+ This driver provides supports for the wireless buttons found on some AMD,
424+ HP, & Xioami laptops.
424425 On such systems the driver should load automatically (via ACPI alias).
425426
426427 To compile this driver as a module, choose M here: the module will
427− be called hp-wireless.
428+ be called wireless-hotkey.
428429
429430 config HP_WMI
430431 tristate "HP WMI extras"
@@ -639,6 +640,19 @@ config THINKPAD_ACPI_HOTKEY_POLL · 208 unmodified lines
639640 If you are not sure, say Y here. The driver enables polling only if
640641 it is strictly necessary to do so.
641642
643+config THINKPAD_LMI
644+ tristate "Lenovo WMI-based systems management driver"
645+ depends on ACPI_WMI
646+ select FW_ATTR_CLASS
647+ help
648+ This driver allows changing BIOS settings on Lenovo machines whose
649+ BIOS support the WMI interface.
650+
651+ To compile this driver as a module, choose M here: the module will
652+ be called think-lmi.
653+
654+source "drivers/platform/x86/intel/Kconfig"
655+
642656 config INTEL_ATOMISP2_LED
643657 tristate "Intel AtomISP2 camera LED driver"
644658 depends on GPIOLIB && LEDS_GPIO
@@ -673,30 +687,6 @@ config INTEL_ATOMISP2_PM · 28 unmodified lines
673687 To compile this driver as a module, choose M here: the module
674688 will be called intel_atomisp2_pm.
675689
676−config INTEL_CHT_INT33FE
677− tristate "Intel Cherry Trail ACPI INT33FE Driver"
678− depends on X86 && ACPI && I2C && REGULATOR
679− depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m)
680− depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m)
681− depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m)
682− help
683− This driver add support for the INT33FE ACPI device found on
684− some Intel Cherry Trail devices.
685−
686− There are two kinds of INT33FE ACPI device possible: for hardware
687− with USB Type-C and Micro-B connectors. This driver supports both.
688−
689− The INT33FE ACPI device has a CRS table with I2cSerialBusV2
690− resources for Fuel Gauge Controller and (in the Type-C variant)
691− FUSB302 USB Type-C Controller and PI3USB30532 USB switch.
692− This driver instantiates i2c-clients for these, so that standard
693− i2c drivers for these chips can bind to the them.
694−
695− If you enable this driver it is advised to also select
696− CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B
697− device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m
698− for Type-C device.
699−
700690 config INTEL_HID_EVENT
701691 tristate "INTEL HID Event"
702692 depends on ACPI
@@ -1076,6 +1066,9 @@ config TOUCHSCREEN_DMI · 373 unmodified lines
10761066 the OS-image for the device. This option supplies the missing info.
10771067 Enable this for x86 tablets with Silead or Chipone touchscreens.
10781068
1069+config FW_ATTR_CLASS
1070+ tristate
1071+
10791072 config INTEL_IMR
10801073 bool "Intel Isolated Memory Region support"
10811074 depends on X86_INTEL_QUARK && IOSF_MBI
drivers/platform/x86/Makefile+5−5
@@ -52,7 +52,6 @@ obj-$(CONFIG_GPD_POCKET_FAN) += gpd-pocket-fan.o · 51 unmodified lines
5252
5353 # Hewlett Packard
5454 obj-$(CONFIG_HP_ACCEL) += hp_accel.o
55−obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o
5655 obj-$(CONFIG_HP_WMI) += hp-wmi.o
5756 obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
5857
@@ -64,14 +63,13 @@ obj-$(CONFIG_IBM_RTL) += ibm_rtl.o · 5 unmodified lines
6463 obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
6564 obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
6665 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
66+obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
6767
6868 # Intel
69+obj-$(CONFIG_X86_PLATFORM_DRIVERS_INTEL) += intel/
70+
6971 obj-$(CONFIG_INTEL_ATOMISP2_LED) += intel_atomisp2_led.o
7072 obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
71−obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
72−intel_cht_int33fe-objs := intel_cht_int33fe_common.o \
73− intel_cht_int33fe_typec.o \
74− intel_cht_int33fe_microb.o
7573 obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
7674 obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
7775 obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
@@ -112,9 +110,11 @@ obj-$(CONFIG_SYSTEM76_ACPI) += system76_acpi.o · 34 unmodified lines
112110 obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
113111
114112 # Platform drivers
113+obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o
115114 obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
116115 obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
117116 obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
117+obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
118118
119119 # Intel uncore drivers
120120 obj-$(CONFIG_INTEL_IPS) += intel_ips.o
drivers/platform/x86/asus-nb-wmi.c+0−77
@@ -110,11 +110,6 @@ static struct quirk_entry quirk_asus_forceals = { · 109 unmodified lines
110110 .wmi_force_als_set = true,
111111 };
112112
113−static struct quirk_entry quirk_asus_vendor_backlight = {
114− .wmi_backlight_power = true,
115− .wmi_backlight_set_devstate = true,
116−};
117−
118113 static struct quirk_entry quirk_asus_use_kbd_dock_devid = {
119114 .use_kbd_dock_devid = true,
120115 };
@@ -425,78 +420,6 @@ static const struct dmi_system_id asus_quirks[] = { · 304 unmodified lines
425420 },
426421 .driver_data = &quirk_asus_forceals,
427422 },
428− {
429− .callback = dmi_matched,
430− .ident = "ASUSTeK COMPUTER INC. GA401IH",
431− .matches = {
432− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
433− DMI_MATCH(DMI_PRODUCT_NAME, "GA401IH"),
434− },
435− .driver_data = &quirk_asus_vendor_backlight,
436− },
437− {
438− .callback = dmi_matched,
439− .ident = "ASUSTeK COMPUTER INC. GA401II",
440− .matches = {
441− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
442− DMI_MATCH(DMI_PRODUCT_NAME, "GA401II"),
443− },
444− .driver_data = &quirk_asus_vendor_backlight,
445− },
446− {
447− .callback = dmi_matched,
448− .ident = "ASUSTeK COMPUTER INC. GA401IU",
449− .matches = {
450− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
451− DMI_MATCH(DMI_PRODUCT_NAME, "GA401IU"),
452− },
453− .driver_data = &quirk_asus_vendor_backlight,
454− },
455− {
456− .callback = dmi_matched,
457− .ident = "ASUSTeK COMPUTER INC. GA401IV",
458− .matches = {
459− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
460− DMI_MATCH(DMI_PRODUCT_NAME, "GA401IV"),
461− },
462− .driver_data = &quirk_asus_vendor_backlight,
463− },
464− {
465− .callback = dmi_matched,
466− .ident = "ASUSTeK COMPUTER INC. GA401IVC",
467− .matches = {
468− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
469− DMI_MATCH(DMI_PRODUCT_NAME, "GA401IVC"),
470− },
471− .driver_data = &quirk_asus_vendor_backlight,
472− },
473− {
474− .callback = dmi_matched,
475− .ident = "ASUSTeK COMPUTER INC. GA502II",
476− .matches = {
477− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
478− DMI_MATCH(DMI_PRODUCT_NAME, "GA502II"),
479− },
480− .driver_data = &quirk_asus_vendor_backlight,
481− },
482− {
483− .callback = dmi_matched,
484− .ident = "ASUSTeK COMPUTER INC. GA502IU",
485− .matches = {
486− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
487− DMI_MATCH(DMI_PRODUCT_NAME, "GA502IU"),
488− },
489− .driver_data = &quirk_asus_vendor_backlight,
490− },
491− {
492− .callback = dmi_matched,
493− .ident = "ASUSTeK COMPUTER INC. GA502IV",
494− .matches = {
495− DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
496− DMI_MATCH(DMI_PRODUCT_NAME, "GA502IV"),
497− },
498− .driver_data = &quirk_asus_vendor_backlight,
499− },
500423 {
501424 .callback = dmi_matched,
502425 .ident = "Asus Transformer T100TA / T100HA / T100CHI",
drivers/platform/x86/dell/Kconfig+10−1
@@ -5,7 +5,6 @@ · 4 unmodified lines
55
66 menuconfig X86_PLATFORM_DRIVERS_DELL
77 bool "Dell X86 Platform Specific Device Drivers"
8− default n
98 depends on X86_PLATFORM_DEVICES
109 help
1110 Say Y here to get to see options for device drivers for various
@@ -53,6 +52,7 @@ config DELL_LAPTOP · 41 unmodified lines
5352 depends on BACKLIGHT_CLASS_DEVICE
5453 depends on ACPI_VIDEO || ACPI_VIDEO = n
5554 depends on RFKILL || RFKILL = n
55+ depends on DELL_WMI || DELL_WMI = n
5656 depends on SERIO_I8042
5757 depends on DELL_SMBIOS
5858 select POWER_SUPPLY
@@ -164,6 +164,14 @@ config DELL_WMI · 105 unmodified lines
164164 To compile this driver as a module, choose M here: the module will
165165 be called dell-wmi.
166166
167+config DELL_WMI_PRIVACY
168+ bool "Dell WMI Hardware Privacy Support"
169+ depends on DELL_WMI
170+ depends on LEDS_TRIGGER_AUDIO
171+ help
172+ This option adds integration with the "Dell Hardware Privacy"
173+ feature of Dell laptops to the dell-wmi driver.
174+
167175 config DELL_WMI_AIO
168176 tristate "WMI Hotkeys for Dell All-In-One series"
169177 default m
@@ -197,6 +205,7 @@ config DELL_WMI_SYSMAN · 27 unmodified lines
197205 depends on ACPI_WMI
198206 depends on DMI
199207 select NLS
208+ select FW_ATTR_CLASS
200209 help
201210 This driver allows changing BIOS settings on many Dell machines from
202211 2018 and newer without the use of any additional software.
drivers/platform/x86/dell/Makefile+2−0
@@ -15,6 +15,8 @@ dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o · 14 unmodified lines
1515 dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
1616 obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
1717 obj-$(CONFIG_DELL_WMI) += dell-wmi.o
18+dell-wmi-objs := dell-wmi-base.o
19+dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o
1820 obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
1921 obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
2022 obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
drivers/platform/x86/dell/dcdbas.c+1−2
@@ -394,8 +394,7 @@ static int host_control_smi(void) · 393 unmodified lines
394394
395395 /* wait a few to see if it executed */
396396 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
397− while ((cmd_status = inb(PCAT_APM_STATUS_PORT))
398− == ESM_STATUS_CMD_UNSUCCESSFUL) {
397+ while ((s8)inb(PCAT_APM_STATUS_PORT) == ESM_STATUS_CMD_UNSUCCESSFUL) {
399398 num_ticks--;
400399 if (num_ticks == EXPIRED_TIMER)
401400 return -ETIME;
drivers/platform/x86/dell/dell-laptop.c+10−3
@@ -31,6 +31,8 @@ · 30 unmodified lines
3131 #include "dell-rbtn.h"
3232 #include "dell-smbios.h"
3333
34+#include "dell-wmi-privacy.h"
35+
3436 struct quirk_entry {
3537 bool touchpad_led;
3638 bool kbd_led_not_present;
@@ -90,6 +92,7 @@ static struct rfkill *wifi_rfkill; · 53 unmodified lines
9092 static struct rfkill *bluetooth_rfkill;
9193 static struct rfkill *wwan_rfkill;
9294 static bool force_rfkill;
95+static bool micmute_led_registered;
9396
9497 module_param(force_rfkill, bool, 0444);
9598 MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
@@ -2205,11 +2208,13 @@ static int __init dell_init(void) · 2109 unmodified lines
22052208 dell_laptop_register_notifier(&dell_laptop_notifier);
22062209
22072210 if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
2208− dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) {
2211+ dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) &&
2212+ !dell_privacy_has_mic_mute()) {
22092213 micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
22102214 ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
22112215 if (ret < 0)
22122216 goto fail_led;
2217+ micmute_led_registered = true;
22132218 }
22142219
22152220 if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
@@ -2257,7 +2262,8 @@ static int __init dell_init(void) · 41 unmodified lines
22572262 fail_get_brightness:
22582263 backlight_device_unregister(dell_backlight_device);
22592264 fail_backlight:
2260− led_classdev_unregister(&micmute_led_cdev);
2265+ if (micmute_led_registered)
2266+ led_classdev_unregister(&micmute_led_cdev);
22612267 fail_led:
22622268 dell_cleanup_rfkill();
22632269 fail_rfkill:
@@ -2278,7 +2284,8 @@ static void __exit dell_exit(void) · 14 unmodified lines
22782284 touchpad_led_exit();
22792285 kbd_led_exit();
22802286 backlight_device_unregister(dell_backlight_device);
2281− led_classdev_unregister(&micmute_led_cdev);
2287+ if (micmute_led_registered)
2288+ led_classdev_unregister(&micmute_led_cdev);
22822289 dell_cleanup_rfkill();
22832290 if (platform_device) {
22842291 platform_device_unregister(platform_device);
drivers/platform/x86/dell/dell-wmi-base.crenamed from drivers/platform/x86/dell/dell-wmi.c+13−1
@@ -27,6 +27,7 @@ · 26 unmodified lines
2727 #include <acpi/video.h>
2828 #include "dell-smbios.h"
2929 #include "dell-wmi-descriptor.h"
30+#include "dell-wmi-privacy.h"
3031
3132 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
3233 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
@@ -427,7 +428,6 @@ static void dell_wmi_notify(struct wmi_device *wdev, · 394 unmodified lines
427428
428429 switch (buffer_entry[1]) {
429430 case 0x0000: /* One key pressed or event occurred */
430− case 0x0012: /* Event with extended data occurred */
431431 if (len > 2)
432432 dell_wmi_process_key(wdev, buffer_entry[1],
433433 buffer_entry[2]);
@@ -439,6 +439,13 @@ static void dell_wmi_notify(struct wmi_device *wdev, · 5 unmodified lines
439439 dell_wmi_process_key(wdev, buffer_entry[1],
440440 buffer_entry[i]);
441441 break;
442+ case 0x0012:
443+ if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3],
444+ buffer_entry[4]))
445+ /* dell_privacy_process_event has handled the event */;
446+ else if (len > 2)
447+ dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2]);
448+ break;
442449 default: /* Unknown event */
443450 pr_info("Unknown WMI event type 0x%x\n",
444451 (int)buffer_entry[1]);
@@ -747,6 +754,10 @@ static int __init dell_wmi_init(void) · 302 unmodified lines
747754 }
748755 }
749756
757+ err = dell_privacy_register_driver();
758+ if (err)
759+ return err;
760+
750761 return wmi_driver_register(&dell_wmi_driver);
751762 }
752763 late_initcall(dell_wmi_init);
@@ -757,6 +768,7 @@ static void __exit dell_wmi_exit(void) · 4 unmodified lines
757768 dell_wmi_events_set_enabled(false);
758769
759770 wmi_driver_unregister(&dell_wmi_driver);
771+ dell_privacy_unregister_driver();
760772 }
761773 module_exit(dell_wmi_exit);
762774
drivers/platform/x86/dell/dell-wmi-privacy.c+391−0
@@ -0,0 +1,391 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Dell privacy notification driver
4+ *
5+ * Copyright (C) 2021 Dell Inc. All Rights Reserved.
6+ */
7+
8+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9+
10+#include <linux/acpi.h>
11+#include <linux/bitops.h>
12+#include <linux/input.h>
13+#include <linux/input/sparse-keymap.h>
14+#include <linux/list.h>
15+#include <linux/leds.h>
16+#include <linux/module.h>
17+#include <linux/wmi.h>
18+
19+#include "dell-wmi-privacy.h"
20+
21+#define DELL_PRIVACY_GUID "6932965F-1671-4CEB-B988-D3AB0A901919"
22+#define MICROPHONE_STATUS BIT(0)
23+#define CAMERA_STATUS BIT(1)
24+#define DELL_PRIVACY_AUDIO_EVENT 0x1
25+#define DELL_PRIVACY_CAMERA_EVENT 0x2
26+#define led_to_priv(c) container_of(c, struct privacy_wmi_data, cdev)
27+
28+/*
29+ * The wmi_list is used to store the privacy_priv struct with mutex protecting
30+ */
31+static LIST_HEAD(wmi_list);
32+static DEFINE_MUTEX(list_mutex);
33+
34+struct privacy_wmi_data {
35+ struct input_dev *input_dev;
36+ struct wmi_device *wdev;
37+ struct list_head list;
38+ struct led_classdev cdev;
39+ u32 features_present;
40+ u32 last_status;
41+};
42+
43+/* DELL Privacy Type */
44+enum dell_hardware_privacy_type {
45+ DELL_PRIVACY_TYPE_AUDIO = 0,
46+ DELL_PRIVACY_TYPE_CAMERA,
47+ DELL_PRIVACY_TYPE_SCREEN,
48+ DELL_PRIVACY_TYPE_MAX,
49+};
50+
51+static const char * const privacy_types[DELL_PRIVACY_TYPE_MAX] = {
52+ [DELL_PRIVACY_TYPE_AUDIO] = "Microphone",
53+ [DELL_PRIVACY_TYPE_CAMERA] = "Camera Shutter",
54+ [DELL_PRIVACY_TYPE_SCREEN] = "ePrivacy Screen",
55+};
56+
57+/*
58+ * Keymap for WMI privacy events of type 0x0012
59+ */
60+static const struct key_entry dell_wmi_keymap_type_0012[] = {
61+ /* privacy mic mute */
62+ { KE_KEY, 0x0001, { KEY_MICMUTE } },
63+ /* privacy camera mute */
64+ { KE_SW, 0x0002, { SW_CAMERA_LENS_COVER } },
65+ { KE_END, 0},
66+};
67+
68+bool dell_privacy_has_mic_mute(void)
69+{
70+ struct privacy_wmi_data *priv;
71+
72+ mutex_lock(&list_mutex);
73+ priv = list_first_entry_or_null(&wmi_list,
74+ struct privacy_wmi_data,
75+ list);
76+ mutex_unlock(&list_mutex);
77+
78+ return priv && (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO));
79+}
80+EXPORT_SYMBOL_GPL(dell_privacy_has_mic_mute);
81+
82+/*
83+ * The flow of privacy event:
84+ * 1) User presses key. HW does stuff with this key (timeout is started)
85+ * 2) WMI event is emitted from BIOS
86+ * 3) WMI event is received by dell-privacy
87+ * 4) KEY_MICMUTE emitted from dell-privacy
88+ * 5) Userland picks up key and modifies kcontrol for SW mute
89+ * 6) Codec kernel driver catches and calls ledtrig_audio_set which will call
90+ * led_set_brightness() on the LED registered by dell_privacy_leds_setup()
91+ * 7) dell-privacy notifies EC, the timeout is cancelled and the HW mute activates.
92+ * If the EC is not notified then the HW mic mute will activate when the timeout
93+ * triggers, just a bit later than with the active ack.
94+ */
95+bool dell_privacy_process_event(int type, int code, int status)
96+{
97+ struct privacy_wmi_data *priv;
98+ const struct key_entry *key;
99+ bool ret = false;
100+
101+ mutex_lock(&list_mutex);
102+ priv = list_first_entry_or_null(&wmi_list,
103+ struct privacy_wmi_data,
104+ list);
105+ if (!priv)
106+ goto error;
107+
108+ key = sparse_keymap_entry_from_scancode(priv->input_dev, (type << 16) | code);
109+ if (!key) {
110+ dev_warn(&priv->wdev->dev, "Unknown key with type 0x%04x and code 0x%04x pressed\n",
111+ type, code);
112+ goto error;
113+ }
114+ dev_dbg(&priv->wdev->dev, "Key with type 0x%04x and code 0x%04x pressed\n", type, code);
115+
116+ switch (code) {
117+ case DELL_PRIVACY_AUDIO_EVENT: /* Mic mute */
118+ case DELL_PRIVACY_CAMERA_EVENT: /* Camera mute */
119+ priv->last_status = status;
120+ sparse_keymap_report_entry(priv->input_dev, key, 1, true);
121+ ret = true;
122+ break;
123+ default:
124+ dev_dbg(&priv->wdev->dev, "unknown event type 0x%04x 0x%04x\n", type, code);
125+ }
126+
127+error:
128+ mutex_unlock(&list_mutex);
129+ return ret;
130+}
131+
132+static ssize_t dell_privacy_supported_type_show(struct device *dev,
133+ struct device_attribute *attr,
134+ char *buf)
135+{
136+ struct privacy_wmi_data *priv = dev_get_drvdata(dev);
137+ enum dell_hardware_privacy_type type;
138+ u32 privacy_list;
139+ int len = 0;
140+
141+ privacy_list = priv->features_present;
142+ for (type = DELL_PRIVACY_TYPE_AUDIO; type < DELL_PRIVACY_TYPE_MAX; type++) {
143+ if (privacy_list & BIT(type))
144+ len += sysfs_emit_at(buf, len, "[%s] [supported]\n", privacy_types[type]);
145+ else
146+ len += sysfs_emit_at(buf, len, "[%s] [unsupported]\n", privacy_types[type]);
147+ }
148+
149+ return len;
150+}
151+
152+static ssize_t dell_privacy_current_state_show(struct device *dev,
153+ struct device_attribute *attr,
154+ char *buf)
155+{
156+ struct privacy_wmi_data *priv = dev_get_drvdata(dev);
157+ u32 privacy_supported = priv->features_present;
158+ enum dell_hardware_privacy_type type;
159+ u32 privacy_state = priv->last_status;
160+ int len = 0;
161+
162+ for (type = DELL_PRIVACY_TYPE_AUDIO; type < DELL_PRIVACY_TYPE_MAX; type++) {
163+ if (privacy_supported & BIT(type)) {
164+ if (privacy_state & BIT(type))
165+ len += sysfs_emit_at(buf, len, "[%s] [unmuted]\n", privacy_types[type]);
166+ else
167+ len += sysfs_emit_at(buf, len, "[%s] [muted]\n", privacy_types[type]);
168+ }
169+ }
170+
171+ return len;
172+}
173+
174+static DEVICE_ATTR_RO(dell_privacy_supported_type);
175+static DEVICE_ATTR_RO(dell_privacy_current_state);
176+
177+static struct attribute *privacy_attributes[] = {
178+ &dev_attr_dell_privacy_supported_type.attr,
179+ &dev_attr_dell_privacy_current_state.attr,
180+ NULL,
181+};
182+
183+static const struct attribute_group privacy_attribute_group = {
184+ .attrs = privacy_attributes
185+};
186+
187+/*
188+ * Describes the Device State class exposed by BIOS which can be consumed by
189+ * various applications interested in knowing the Privacy feature capabilities.
190+ * class DeviceState
191+ * {
192+ * [key, read] string InstanceName;
193+ * [read] boolean ReadOnly;
194+ *
195+ * [WmiDataId(1), read] uint32 DevicesSupported;
196+ * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen
197+ *
198+ * [WmiDataId(2), read] uint32 CurrentState;
199+ * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen
200+ * };
201+ */
202+static int get_current_status(struct wmi_device *wdev)
203+{
204+ struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev);
205+ union acpi_object *obj_present;
206+ u32 *buffer;
207+ int ret = 0;
208+
209+ if (!priv) {
210+ dev_err(&wdev->dev, "dell privacy priv is NULL\n");
211+ return -EINVAL;
212+ }
213+ /* check privacy support features and device states */
214+ obj_present = wmidev_block_query(wdev, 0);
215+ if (!obj_present) {
216+ dev_err(&wdev->dev, "failed to read Binary MOF\n");
217+ return -EIO;
218+ }
219+
220+ if (obj_present->type != ACPI_TYPE_BUFFER) {
221+ dev_err(&wdev->dev, "Binary MOF is not a buffer!\n");
222+ ret = -EIO;
223+ goto obj_free;
224+ }
225+ /* Although it's not technically a failure, this would lead to
226+ * unexpected behavior
227+ */
228+ if (obj_present->buffer.length != 8) {
229+ dev_err(&wdev->dev, "Dell privacy buffer has unexpected length (%d)!\n",
230+ obj_present->buffer.length);
231+ ret = -EINVAL;
232+ goto obj_free;
233+ }
234+ buffer = (u32 *)obj_present->buffer.pointer;
235+ priv->features_present = buffer[0];
236+ priv->last_status = buffer[1];
237+
238+obj_free:
239+ kfree(obj_present);
240+ return ret;
241+}
242+
243+static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev,
244+ enum led_brightness brightness)
245+{
246+ struct privacy_wmi_data *priv = led_to_priv(led_cdev);
247+ static char *acpi_method = (char *)"ECAK";
248+ acpi_status status;
249+ acpi_handle handle;
250+
251+ handle = ec_get_handle();
252+ if (!handle)
253+ return -EIO;
254+
255+ if (!acpi_has_method(handle, acpi_method))
256+ return -EIO;
257+
258+ status = acpi_evaluate_object(handle, acpi_method, NULL, NULL);
259+ if (ACPI_FAILURE(status)) {
260+ dev_err(&priv->wdev->dev, "Error setting privacy EC ack value: %s\n",
261+ acpi_format_exception(status));
262+ return -EIO;
263+ }
264+
265+ return 0;
266+}
267+
268+/*
269+ * Pressing the mute key activates a time delayed circuit to physically cut
270+ * off the mute. The LED is in the same circuit, so it reflects the true
271+ * state of the HW mute. The reason for the EC "ack" is so that software
272+ * can first invoke a SW mute before the HW circuit is cut off. Without SW
273+ * cutting this off first does not affect the time delayed muting or status
274+ * of the LED but there is a possibility of a "popping" noise.
275+ *
276+ * If the EC receives the SW ack, the circuit will be activated before the
277+ * delay completed.
278+ *
279+ * Exposing as an LED device allows the codec drivers notification path to
280+ * EC ACK to work
281+ */
282+static int dell_privacy_leds_setup(struct device *dev)
283+{
284+ struct privacy_wmi_data *priv = dev_get_drvdata(dev);
285+
286+ priv->cdev.name = "dell-privacy::micmute";
287+ priv->cdev.max_brightness = 1;
288+ priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set;
289+ priv->cdev.default_trigger = "audio-micmute";
290+ priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
291+ return devm_led_classdev_register(dev, &priv->cdev);
292+}
293+
294+static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
295+{
296+ struct privacy_wmi_data *priv;
297+ struct key_entry *keymap;
298+ int ret, i;
299+
300+ ret = wmi_has_guid(DELL_PRIVACY_GUID);
301+ if (!ret)
302+ pr_debug("Unable to detect available Dell privacy devices!\n");
303+
304+ priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
305+ if (!priv)
306+ return -ENOMEM;
307+
308+ dev_set_drvdata(&wdev->dev, priv);
309+ priv->wdev = wdev;
310+ /* create evdev passing interface */
311+ priv->input_dev = devm_input_allocate_device(&wdev->dev);
312+ if (!priv->input_dev)
313+ return -ENOMEM;
314+
315+ /* remap the wmi keymap event to new keymap */
316+ keymap = kcalloc(ARRAY_SIZE(dell_wmi_keymap_type_0012),
317+ sizeof(struct key_entry), GFP_KERNEL);
318+ if (!keymap)
319+ return -ENOMEM;
320+
321+ /* remap the keymap code with Dell privacy key type 0x12 as prefix
322+ * KEY_MICMUTE scancode will be reported as 0x120001
323+ */
324+ for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) {
325+ keymap[i] = dell_wmi_keymap_type_0012[i];
326+ keymap[i].code |= (0x0012 << 16);
327+ }
328+ ret = sparse_keymap_setup(priv->input_dev, keymap, NULL);
329+ kfree(keymap);
330+ if (ret)
331+ return ret;
332+
333+ priv->input_dev->dev.parent = &wdev->dev;
334+ priv->input_dev->name = "Dell Privacy Driver";
335+ priv->input_dev->id.bustype = BUS_HOST;
336+
337+ ret = input_register_device(priv->input_dev);
338+ if (ret)
339+ return ret;
340+
341+ ret = get_current_status(priv->wdev);
342+ if (ret)
343+ return ret;
344+
345+ ret = devm_device_add_group(&wdev->dev, &privacy_attribute_group);
346+ if (ret)
347+ return ret;
348+
349+ if (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO)) {
350+ ret = dell_privacy_leds_setup(&priv->wdev->dev);
351+ if (ret)
352+ return ret;
353+ }
354+ mutex_lock(&list_mutex);
355+ list_add_tail(&priv->list, &wmi_list);
356+ mutex_unlock(&list_mutex);
357+ return 0;
358+}
359+
360+static void dell_privacy_wmi_remove(struct wmi_device *wdev)
361+{
362+ struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev);
363+
364+ mutex_lock(&list_mutex);
365+ list_del(&priv->list);
366+ mutex_unlock(&list_mutex);
367+}
368+
369+static const struct wmi_device_id dell_wmi_privacy_wmi_id_table[] = {
370+ { .guid_string = DELL_PRIVACY_GUID },
371+ { },
372+};
373+
374+static struct wmi_driver dell_privacy_wmi_driver = {
375+ .driver = {
376+ .name = "dell-privacy",
377+ },
378+ .probe = dell_privacy_wmi_probe,
379+ .remove = dell_privacy_wmi_remove,
380+ .id_table = dell_wmi_privacy_wmi_id_table,
381+};
382+
383+int dell_privacy_register_driver(void)
384+{
385+ return wmi_driver_register(&dell_privacy_wmi_driver);
386+}
387+
388+void dell_privacy_unregister_driver(void)
389+{
390+ wmi_driver_unregister(&dell_privacy_wmi_driver);
391+}
drivers/platform/x86/dell/dell-wmi-privacy.h+36−0
@@ -0,0 +1,36 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/*
3+ * Dell privacy notification driver
4+ *
5+ * Copyright (C) 2021 Dell Inc. All Rights Reserved.
6+ */
7+
8+#ifndef _DELL_PRIVACY_WMI_H_
9+#define _DELL_PRIVACY_WMI_H_
10+
11+#if IS_ENABLED(CONFIG_DELL_WMI_PRIVACY)
12+bool dell_privacy_has_mic_mute(void);
13+bool dell_privacy_process_event(int type, int code, int status);
14+int dell_privacy_register_driver(void);
15+void dell_privacy_unregister_driver(void);
16+#else /* CONFIG_DELL_PRIVACY */
17+static inline bool dell_privacy_has_mic_mute(void)
18+{
19+ return false;
20+}
21+
22+static inline bool dell_privacy_process_event(int type, int code, int status)
23+{
24+ return false;
25+}
26+
27+static inline int dell_privacy_register_driver(void)
28+{
29+ return 0;
30+}
31+
32+static inline void dell_privacy_unregister_driver(void)
33+{
34+}
35+#endif /* CONFIG_DELL_PRIVACY */
36+#endif
drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h+4−1
@@ -152,12 +152,15 @@ static ssize_t curr_val##_store(struct kobject *kobj, \ · 151 unmodified lines
152152 return ret ? ret : count; \
153153 }
154154
155+#define check_property_type(attr, prop, valuetype) \
156+ (attr##_obj[prop].type != valuetype)
157+
155158 union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string);
156159 int get_instance_count(const char *guid_string);
157160 void strlcpy_attr(char *dest, char *src);
158161
159162 int populate_enum_data(union acpi_object *enumeration_obj, int instance_id,
160− struct kobject *attr_name_kobj);
163+ struct kobject *attr_name_kobj, u32 enum_property_count);
161164 int alloc_enum_data(void);
162165 void exit_enum_attributes(void);
163166
drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c+34−5
@@ -132,39 +132,68 @@ int alloc_enum_data(void) · 131 unmodified lines
132132 * @enumeration_obj: ACPI object with enumeration data
133133 * @instance_id: The instance to enumerate
134134 * @attr_name_kobj: The parent kernel object
135+ * @enum_property_count: Total properties count under enumeration type
135136 */
136137 int populate_enum_data(union acpi_object *enumeration_obj, int instance_id,
137− struct kobject *attr_name_kobj)
138+ struct kobject *attr_name_kobj, u32 enum_property_count)
138139 {
139140 int i, next_obj, value_modifier_count, possible_values_count;
140141
141142 wmi_priv.enumeration_data[instance_id].attr_name_kobj = attr_name_kobj;
143+ if (check_property_type(enumeration, ATTR_NAME, ACPI_TYPE_STRING))
144+ return -EINVAL;
142145 strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name,
143146 enumeration_obj[ATTR_NAME].string.pointer);
147+ if (check_property_type(enumeration, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
148+ return -EINVAL;
144149 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code,
145150 enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer);
151+ if (check_property_type(enumeration, DISPLAY_NAME, ACPI_TYPE_STRING))
152+ return -EINVAL;
146153 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name,
147154 enumeration_obj[DISPLAY_NAME].string.pointer);
155+ if (check_property_type(enumeration, DEFAULT_VAL, ACPI_TYPE_STRING))
156+ return -EINVAL;
148157 strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value,
149158 enumeration_obj[DEFAULT_VAL].string.pointer);
159+ if (check_property_type(enumeration, MODIFIER, ACPI_TYPE_STRING))
160+ return -EINVAL;
150161 strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier,
151162 enumeration_obj[MODIFIER].string.pointer);
152163
153164 next_obj = MODIFIER + 1;
154165
155− value_modifier_count = (uintptr_t)enumeration_obj[next_obj].string.pointer;
166+ if (next_obj >= enum_property_count)
167+ return -EINVAL;
168+
169+ if (check_property_type(enumeration, next_obj, ACPI_TYPE_INTEGER))
170+ return -EINVAL;
171+ value_modifier_count = (uintptr_t)enumeration_obj[next_obj++].string.pointer;
156172
157173 for (i = 0; i < value_modifier_count; i++) {
174+ if (next_obj >= enum_property_count)
175+ return -EINVAL;
176+ if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING))
177+ return -EINVAL;
158178 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier,
159− enumeration_obj[++next_obj].string.pointer);
179+ enumeration_obj[next_obj++].string.pointer);
160180 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";");
161181 }
162182
163− possible_values_count = (uintptr_t) enumeration_obj[++next_obj].string.pointer;
183+ if (next_obj >= enum_property_count)
184+ return -EINVAL;
185+
186+ if (check_property_type(enumeration, next_obj, ACPI_TYPE_INTEGER))
187+ return -EINVAL;
188+ possible_values_count = (uintptr_t) enumeration_obj[next_obj++].string.pointer;
164189
165190 for (i = 0; i < possible_values_count; i++) {
191+ if (next_obj >= enum_property_count)
192+ return -EINVAL;
193+ if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING))
194+ return -EINVAL;
166195 strcat(wmi_priv.enumeration_data[instance_id].possible_values,
167− enumeration_obj[++next_obj].string.pointer);
196+ enumeration_obj[next_obj++].string.pointer);
168197 strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";");
169198 }
170199
drivers/platform/x86/dell/dell-wmi-sysman/int-attributes.c+16−0
@@ -141,20 +141,36 @@ int populate_int_data(union acpi_object *integer_obj, int instance_id, · 140 unmodified lines
141141 struct kobject *attr_name_kobj)
142142 {
143143 wmi_priv.integer_data[instance_id].attr_name_kobj = attr_name_kobj;
144+ if (check_property_type(integer, ATTR_NAME, ACPI_TYPE_STRING))
145+ return -EINVAL;
144146 strlcpy_attr(wmi_priv.integer_data[instance_id].attribute_name,
145147 integer_obj[ATTR_NAME].string.pointer);
148+ if (check_property_type(integer, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
149+ return -EINVAL;
146150 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name_language_code,
147151 integer_obj[DISPL_NAME_LANG_CODE].string.pointer);
152+ if (check_property_type(integer, DISPLAY_NAME, ACPI_TYPE_STRING))
153+ return -EINVAL;
148154 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name,
149155 integer_obj[DISPLAY_NAME].string.pointer);
156+ if (check_property_type(integer, DEFAULT_VAL, ACPI_TYPE_INTEGER))
157+ return -EINVAL;
150158 wmi_priv.integer_data[instance_id].default_value =
151159 (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer;
160+ if (check_property_type(integer, MODIFIER, ACPI_TYPE_STRING))
161+ return -EINVAL;
152162 strlcpy_attr(wmi_priv.integer_data[instance_id].dell_modifier,
153163 integer_obj[MODIFIER].string.pointer);
164+ if (check_property_type(integer, MIN_VALUE, ACPI_TYPE_INTEGER))
165+ return -EINVAL;
154166 wmi_priv.integer_data[instance_id].min_value =
155167 (uintptr_t)integer_obj[MIN_VALUE].string.pointer;
168+ if (check_property_type(integer, MAX_VALUE, ACPI_TYPE_INTEGER))
169+ return -EINVAL;
156170 wmi_priv.integer_data[instance_id].max_value =
157171 (uintptr_t)integer_obj[MAX_VALUE].string.pointer;
172+ if (check_property_type(integer, SCALAR_INCR, ACPI_TYPE_INTEGER))
173+ return -EINVAL;
158174 wmi_priv.integer_data[instance_id].scalar_increment =
159175 (uintptr_t)integer_obj[SCALAR_INCR].string.pointer;
160176
drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c+6−0
@@ -159,10 +159,16 @@ int alloc_po_data(void) · 158 unmodified lines
159159 int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj)
160160 {
161161 wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj;
162+ if (check_property_type(po, ATTR_NAME, ACPI_TYPE_STRING))
163+ return -EINVAL;
162164 strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name,
163165 po_obj[ATTR_NAME].string.pointer);
166+ if (check_property_type(po, MIN_PASS_LEN, ACPI_TYPE_INTEGER))
167+ return -EINVAL;
164168 wmi_priv.po_data[instance_id].min_password_length =
165169 (uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;
170+ if (check_property_type(po, MAX_PASS_LEN, ACPI_TYPE_INTEGER))
171+ return -EINVAL;
166172 wmi_priv.po_data[instance_id].max_password_length =
167173 (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;
168174
drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c+2−2
@@ -95,9 +95,9 @@ int set_new_password(const char *password_type, const char *new) · 94 unmodified lines
9595
9696 print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
9797 ret = call_password_interface(wmi_priv.password_attr_wdev, buffer, buffer_size);
98− /* clear current_password here and use user input from wmi_priv.current_password */
98+ /* on success copy the new password to current password */
9999 if (!ret)
100− memset(current_password, 0, MAX_BUFF);
100+ strscpy(current_password, new, MAX_BUFF);
101101 /* explain to user the detailed failure reason */
102102 else if (ret == -EOPNOTSUPP)
103103 dev_err(&wmi_priv.password_attr_wdev->dev, "admin password must be configured\n");
drivers/platform/x86/dell/dell-wmi-sysman/string-attributes.c+15−1
@@ -118,24 +118,38 @@ int alloc_str_data(void) · 117 unmodified lines
118118
119119 /**
120120 * populate_str_data() - Populate all properties of an instance under string attribute
121− * @str_obj: ACPI object with integer data
121+ * @str_obj: ACPI object with string data
122122 * @instance_id: The instance to enumerate
123123 * @attr_name_kobj: The parent kernel object
124124 */
125125 int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj)
126126 {
127127 wmi_priv.str_data[instance_id].attr_name_kobj = attr_name_kobj;
128+ if (check_property_type(str, ATTR_NAME, ACPI_TYPE_STRING))
129+ return -EINVAL;
128130 strlcpy_attr(wmi_priv.str_data[instance_id].attribute_name,
129131 str_obj[ATTR_NAME].string.pointer);
132+ if (check_property_type(str, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
133+ return -EINVAL;
130134 strlcpy_attr(wmi_priv.str_data[instance_id].display_name_language_code,
131135 str_obj[DISPL_NAME_LANG_CODE].string.pointer);
136+ if (check_property_type(str, DISPLAY_NAME, ACPI_TYPE_STRING))
137+ return -EINVAL;
132138 strlcpy_attr(wmi_priv.str_data[instance_id].display_name,
133139 str_obj[DISPLAY_NAME].string.pointer);
140+ if (check_property_type(str, DEFAULT_VAL, ACPI_TYPE_STRING))
141+ return -EINVAL;
134142 strlcpy_attr(wmi_priv.str_data[instance_id].default_value,
135143 str_obj[DEFAULT_VAL].string.pointer);
144+ if (check_property_type(str, MODIFIER, ACPI_TYPE_STRING))
145+ return -EINVAL;
136146 strlcpy_attr(wmi_priv.str_data[instance_id].dell_modifier,
137147 str_obj[MODIFIER].string.pointer);
148+ if (check_property_type(str, MIN_LEN, ACPI_TYPE_INTEGER))
149+ return -EINVAL;
138150 wmi_priv.str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer;
151+ if (check_property_type(str, MAX_LEN, ACPI_TYPE_INTEGER))
152+ return -EINVAL;
139153 wmi_priv.str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer;
140154
141155 return sysfs_create_group(attr_name_kobj, &str_attr_group);
drivers/platform/x86/dell/dell-wmi-sysman/sysman.c+10−11
@@ -13,14 +13,11 @@ · 12 unmodified lines
1313 #include <linux/kernel.h>
1414 #include <linux/wmi.h>
1515 #include "dell-wmi-sysman.h"
16+#include "../../firmware_attributes_class.h"
1617
1718 #define MAX_TYPES 4
1819 #include <linux/nls.h>
1920
20−static struct class firmware_attributes_class = {
21− .name = "firmware-attributes",
22−};
23−
2421 struct wmi_sysman_priv wmi_priv = {
2522 .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex),
2623 };
@@ -28,6 +25,7 @@ struct wmi_sysman_priv wmi_priv = { · 1 unmodified line
2825 /* reset bios to defaults */
2926 static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
3027 static int reset_option = -1;
28+static struct class *fw_attr_class;
3129
3230
3331 /**
@@ -481,7 +479,8 @@ static int init_bios_attributes(int attr_type, const char *guid) · 447 unmodified lines
481479 /* enumerate all of this attribute */
482480 switch (attr_type) {
483481 case ENUM:
484− retval = populate_enum_data(elements, instance_id, attr_name_kobj);
482+ retval = populate_enum_data(elements, instance_id, attr_name_kobj,
483+ obj->package.count);
485484 break;
486485 case INT:
487486 retval = populate_int_data(elements, instance_id, attr_name_kobj);
@@ -541,11 +540,11 @@ static int __init sysman_init(void) · 53 unmodified lines
541540 goto err_exit_bios_attr_pass_interface;
542541 }
543542
544− ret = class_register(&firmware_attributes_class);
543+ ret = fw_attributes_class_get(&fw_attr_class);
545544 if (ret)
546545 goto err_exit_bios_attr_pass_interface;
547546
548− wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
547+ wmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
549548 NULL, "%s", DRIVER_NAME);
550549 if (IS_ERR(wmi_priv.class_dev)) {
551550 ret = PTR_ERR(wmi_priv.class_dev);
@@ -602,10 +601,10 @@ err_release_attributes_data: · 50 unmodified lines
602601 release_attributes_data();
603602
604603 err_destroy_classdev:
605− device_destroy(&firmware_attributes_class, MKDEV(0, 0));
604+ device_destroy(fw_attr_class, MKDEV(0, 0));
606605
607606 err_unregister_class:
608− class_unregister(&firmware_attributes_class);
607+ fw_attributes_class_put();
609608
610609 err_exit_bios_attr_pass_interface:
611610 exit_bios_attr_pass_interface();
@@ -619,8 +618,8 @@ err_exit_bios_attr_set_interface: · 7 unmodified lines
619618 static void __exit sysman_exit(void)
620619 {
621620 release_attributes_data();
622− device_destroy(&firmware_attributes_class, MKDEV(0, 0));
623− class_unregister(&firmware_attributes_class);
621+ device_destroy(fw_attr_class, MKDEV(0, 0));
622+ fw_attributes_class_put();
624623 exit_bios_attr_set_interface();
625624 exit_bios_attr_pass_interface();
626625 }
drivers/platform/x86/firmware_attributes_class.c+52−0
@@ -0,0 +1,52 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+/* Firmware attributes class helper module */
4+
5+#include <linux/mutex.h>
6+#include <linux/device/class.h>
7+#include <linux/module.h>
8+#include "firmware_attributes_class.h"
9+
10+static DEFINE_MUTEX(fw_attr_lock);
11+static int fw_attr_inuse;
12+
13+static struct class firmware_attributes_class = {
14+ .name = "firmware-attributes",
15+};
16+
17+int fw_attributes_class_get(struct class **fw_attr_class)
18+{
19+ int err;
20+
21+ mutex_lock(&fw_attr_lock);
22+ if (!fw_attr_inuse) { /*first time class is being used*/
23+ err = class_register(&firmware_attributes_class);
24+ if (err) {
25+ mutex_unlock(&fw_attr_lock);
26+ return err;
27+ }
28+ }
29+ fw_attr_inuse++;
30+ *fw_attr_class = &firmware_attributes_class;
31+ mutex_unlock(&fw_attr_lock);
32+ return 0;
33+}
34+EXPORT_SYMBOL_GPL(fw_attributes_class_get);
35+
36+int fw_attributes_class_put(void)
37+{
38+ mutex_lock(&fw_attr_lock);
39+ if (!fw_attr_inuse) {
40+ mutex_unlock(&fw_attr_lock);
41+ return -EINVAL;
42+ }
43+ fw_attr_inuse--;
44+ if (!fw_attr_inuse) /* No more consumers */
45+ class_unregister(&firmware_attributes_class);
46+ mutex_unlock(&fw_attr_lock);
47+ return 0;
48+}
49+EXPORT_SYMBOL_GPL(fw_attributes_class_put);
50+
51+MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
52+MODULE_LICENSE("GPL");
drivers/platform/x86/firmware_attributes_class.h+11−0
@@ -0,0 +1,11 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+
3+/* Firmware attributes class helper module */
4+
5+#ifndef FW_ATTR_CLASS_H
6+#define FW_ATTR_CLASS_H
7+
8+int fw_attributes_class_get(struct class **fw_attr_class);
9+int fw_attributes_class_put(void);
10+
11+#endif /* FW_ATTR_CLASS_H */
drivers/platform/x86/hdaps.c+1−1
@@ -462,7 +462,7 @@ static struct attribute *hdaps_attributes[] = { · 461 unmodified lines
462462 NULL,
463463 };
464464
465−static struct attribute_group hdaps_attribute_group = {
465+static const struct attribute_group hdaps_attribute_group = {
466466 .attrs = hdaps_attributes,
467467 };
468468
drivers/platform/x86/hp-wireless.c+0−102
@@ -1,102 +0,0 @@
1−// SPDX-License-Identifier: GPL-2.0-or-later
2−/*
3− * Airplane mode button for HP & Xiaomi laptops
4− *
5− * Copyright (C) 2014-2017 Alex Hung <alex.hung@canonical.com>
6− */
7−
8−#include <linux/kernel.h>
9−#include <linux/module.h>
10−#include <linux/init.h>
11−#include <linux/input.h>
12−#include <linux/platform_device.h>
13−#include <linux/acpi.h>
14−#include <acpi/acpi_bus.h>
15−
16−MODULE_LICENSE("GPL");
17−MODULE_AUTHOR("Alex Hung");
18−MODULE_ALIAS("acpi*:HPQ6001:*");
19−MODULE_ALIAS("acpi*:WSTADEF:*");
20−MODULE_ALIAS("acpi*:AMDI0051:*");
21−
22−static struct input_dev *hpwl_input_dev;
23−
24−static const struct acpi_device_id hpwl_ids[] = {
25− {"HPQ6001", 0},
26− {"WSTADEF", 0},
27− {"AMDI0051", 0},
28− {"", 0},
29−};
30−
31−static int hp_wireless_input_setup(void)
32−{
33− int err;
34−
35− hpwl_input_dev = input_allocate_device();
36− if (!hpwl_input_dev)
37− return -ENOMEM;
38−
39− hpwl_input_dev->name = "HP Wireless hotkeys";
40− hpwl_input_dev->phys = "hpq6001/input0";
41− hpwl_input_dev->id.bustype = BUS_HOST;
42− hpwl_input_dev->evbit[0] = BIT(EV_KEY);
43− set_bit(KEY_RFKILL, hpwl_input_dev->keybit);
44−
45− err = input_register_device(hpwl_input_dev);
46− if (err)
47− goto err_free_dev;
48−
49− return 0;
50−
51−err_free_dev:
52− input_free_device(hpwl_input_dev);
53− return err;
54−}
55−
56−static void hp_wireless_input_destroy(void)
57−{
58− input_unregister_device(hpwl_input_dev);
59−}
60−
61−static void hpwl_notify(struct acpi_device *acpi_dev, u32 event)
62−{
63− if (event != 0x80) {
64− pr_info("Received unknown event (0x%x)\n", event);
65− return;
66− }
67−
68− input_report_key(hpwl_input_dev, KEY_RFKILL, 1);
69− input_sync(hpwl_input_dev);
70− input_report_key(hpwl_input_dev, KEY_RFKILL, 0);
71− input_sync(hpwl_input_dev);
72−}
73−
74−static int hpwl_add(struct acpi_device *device)
75−{
76− int err;
77−
78− err = hp_wireless_input_setup();
79− if (err)
80− pr_err("Failed to setup hp wireless hotkeys\n");
81−
82− return err;
83−}
84−
85−static int hpwl_remove(struct acpi_device *device)
86−{
87− hp_wireless_input_destroy();
88− return 0;
89−}
90−
91−static struct acpi_driver hpwl_driver = {
92− .name = "hp-wireless",
93− .owner = THIS_MODULE,
94− .ids = hpwl_ids,
95− .ops = {
96− .add = hpwl_add,
97− .remove = hpwl_remove,
98− .notify = hpwl_notify,
99− },
100−};
101−
102−module_acpi_driver(hpwl_driver);
drivers/platform/x86/ideapad-laptop.c+12−0
@@ -1408,6 +1408,18 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) · 1407 unmodified lines
14081408 case 6:
14091409 ideapad_input_report(priv, bit);
14101410 break;
1411+ case 10:
1412+ /*
1413+ * This event gets send on a Yoga 300-11IBR when the EC
1414+ * believes that the device has changed between laptop/
1415+ * tent/stand/tablet mode. The EC relies on getting
1416+ * angle info from 2 accelerometers through a special
1417+ * windows service calling a DSM on the DUAL250E ACPI-
1418+ * device. Linux does not do this, making the laptop/
1419+ * tent/stand/tablet mode info unreliable, so we simply
1420+ * ignore these events.
1421+ */
1422+ break;
14111423 case 9:
14121424 ideapad_sync_rfk_state(priv);
14131425 break;
drivers/platform/x86/intel/Kconfig+22−0
@@ -0,0 +1,22 @@
1+# SPDX-License-Identifier: GPL-2.0-only
2+#
3+# Intel x86 Platform Specific Drivers
4+#
5+
6+menuconfig X86_PLATFORM_DRIVERS_INTEL
7+ bool "Intel x86 Platform Specific Device Drivers"
8+ default y
9+ help
10+ Say Y here to get to see options for device drivers for
11+ various Intel x86 platforms, including vendor-specific
12+ drivers. This option alone does not add any kernel code.
13+
14+ If you say N, all options in this submenu will be skipped
15+ and disabled.
16+
17+if X86_PLATFORM_DRIVERS_INTEL
18+
19+source "drivers/platform/x86/intel/int33fe/Kconfig"
20+source "drivers/platform/x86/intel/int3472/Kconfig"
21+
22+endif # X86_PLATFORM_DRIVERS_INTEL
drivers/platform/x86/intel/Makefile+8−0
@@ -0,0 +1,8 @@
1+# SPDX-License-Identifier: GPL-2.0
2+#
3+# Makefile for drivers/platform/x86/intel
4+# Intel x86 Platform-Specific Drivers
5+#
6+
7+obj-$(CONFIG_INTEL_CHT_INT33FE) += int33fe/
8+obj-$(CONFIG_INTEL_SKL_INT3472) += int3472/
drivers/platform/x86/intel/int33fe/Kconfig+24−0
@@ -0,0 +1,24 @@
1+# SPDX-License-Identifier: GPL-2.0-only
2+config INTEL_CHT_INT33FE
3+ tristate "Intel Cherry Trail ACPI INT33FE Driver"
4+ depends on X86 && ACPI && I2C && REGULATOR
5+ depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m)
6+ depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m)
7+ depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m)
8+ help
9+ This driver add support for the INT33FE ACPI device found on
10+ some Intel Cherry Trail devices.
11+
12+ There are two kinds of INT33FE ACPI device possible: for hardware
13+ with USB Type-C and Micro-B connectors. This driver supports both.
14+
15+ The INT33FE ACPI device has a CRS table with I2cSerialBusV2
16+ resources for Fuel Gauge Controller and (in the Type-C variant)
17+ FUSB302 USB Type-C Controller and PI3USB30532 USB switch.
18+ This driver instantiates i2c-clients for these, so that standard
19+ i2c drivers for these chips can bind to the them.
20+
21+ If you enable this driver it is advised to also select
22+ CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B
23+ device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m
24+ for Type-C device.
drivers/platform/x86/intel/int33fe/Makefile+5−0
@@ -0,0 +1,5 @@
1+# SPDX-License-Identifier: GPL-2.0-only
2+obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
3+intel_cht_int33fe-objs := intel_cht_int33fe_common.o \
4+ intel_cht_int33fe_typec.o \
5+ intel_cht_int33fe_microb.o
drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.crenamed from drivers/platform/x86/intel_cht_int33fe_common.c+0−0

File renamed without changes.

drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.hrenamed from drivers/platform/x86/intel_cht_int33fe_common.h+0−0

File renamed without changes.

drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.crenamed from drivers/platform/x86/intel_cht_int33fe_microb.c+0−0

File renamed without changes.

drivers/platform/x86/intel/int33fe/intel_cht_int33fe_typec.crenamed from drivers/platform/x86/intel_cht_int33fe_typec.c+2−2
@@ -168,8 +168,8 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data) · 167 unmodified lines
168168 return -ENODEV;
169169 }
170170
171− /* Then the DP child device node */
172− data->dp = device_get_named_child_node(&pdev->dev, "DD02");
171+ /* Then the DP-2 child device node */
172+ data->dp = device_get_named_child_node(&pdev->dev, "DD04");
173173 pci_dev_put(pdev);
174174 if (!data->dp)
175175 return -ENODEV;
drivers/platform/x86/intel/int3472/Kconfig+30−0
@@ -0,0 +1,30 @@
1+config INTEL_SKL_INT3472
2+ tristate "Intel SkyLake ACPI INT3472 Driver"
3+ depends on ACPI
4+ depends on COMMON_CLK
5+ depends on I2C
6+ depends on GPIOLIB
7+ depends on REGULATOR
8+ select MFD_CORE
9+ select REGMAP_I2C
10+ help
11+ This driver adds power controller support for the Intel SkyCam
12+ devices found on the Intel SkyLake platforms.
13+
14+ The INT3472 is a camera power controller, a logical device found on
15+ Intel Skylake-based systems that can map to different hardware
16+ devices depending on the platform. On machines designed for Chrome OS
17+ it maps to a TPS68470 camera PMIC. On machines designed for Windows,
18+ it maps to either a TP68470 camera PMIC, a uP6641Q sensor PMIC, or a
19+ set of discrete GPIOs and power gates.
20+
21+ If your device was designed for Chrome OS, this driver will provide
22+ an ACPI OpRegion, which must be available before any of the devices
23+ using it are probed. For this reason, you should select Y if your
24+ device was designed for ChromeOS. For the same reason the
25+ I2C_DESIGNWARE_PLATFORM option must be set to Y too.
26+
27+ Say Y or M here if you have a SkyLake device designed for use
28+ with Windows or ChromeOS. Say N here if you are not sure.
29+
30+ The module will be named "intel-skl-int3472".
drivers/platform/x86/intel/int3472/Makefile+5−0
@@ -0,0 +1,5 @@
1+obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472.o
2+intel_skl_int3472-objs := intel_skl_int3472_common.o \
3+ intel_skl_int3472_discrete.o \
4+ intel_skl_int3472_tps68470.o \
5+ intel_skl_int3472_clk_and_regulator.o
drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c+207−0
@@ -0,0 +1,207 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/* Author: Dan Scally <djrscally@gmail.com> */
3+
4+#include <linux/acpi.h>
5+#include <linux/clkdev.h>
6+#include <linux/clk-provider.h>
7+#include <linux/device.h>
8+#include <linux/gpio/consumer.h>
9+#include <linux/regulator/driver.h>
10+#include <linux/slab.h>
11+
12+#include "intel_skl_int3472_common.h"
13+
14+/*
15+ * The regulators have to have .ops to be valid, but the only ops we actually
16+ * support are .enable and .disable which are handled via .ena_gpiod. Pass an
17+ * empty struct to clear the check without lying about capabilities.
18+ */
19+static const struct regulator_ops int3472_gpio_regulator_ops;
20+
21+static int skl_int3472_clk_prepare(struct clk_hw *hw)
22+{
23+ struct int3472_gpio_clock *clk = to_int3472_clk(hw);
24+
25+ gpiod_set_value_cansleep(clk->ena_gpio, 1);
26+ gpiod_set_value_cansleep(clk->led_gpio, 1);
27+
28+ return 0;
29+}
30+
31+static void skl_int3472_clk_unprepare(struct clk_hw *hw)
32+{
33+ struct int3472_gpio_clock *clk = to_int3472_clk(hw);
34+
35+ gpiod_set_value_cansleep(clk->ena_gpio, 0);
36+ gpiod_set_value_cansleep(clk->led_gpio, 0);
37+}
38+
39+static int skl_int3472_clk_enable(struct clk_hw *hw)
40+{
41+ /*
42+ * We're just turning a GPIO on to enable the clock, which operation
43+ * has the potential to sleep. Given .enable() cannot sleep, but
44+ * .prepare() can, we toggle the GPIO in .prepare() instead. Thus,
45+ * nothing to do here.
46+ */
47+ return 0;
48+}
49+
50+static void skl_int3472_clk_disable(struct clk_hw *hw)
51+{
52+ /* Likewise, nothing to do here... */
53+}
54+
55+static unsigned int skl_int3472_get_clk_frequency(struct int3472_discrete_device *int3472)
56+{
57+ union acpi_object *obj;
58+ unsigned int freq;
59+
60+ obj = skl_int3472_get_acpi_buffer(int3472->sensor, "SSDB");
61+ if (IS_ERR(obj))
62+ return 0; /* report rate as 0 on error */
63+
64+ if (obj->buffer.length < CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET + sizeof(u32)) {
65+ dev_err(int3472->dev, "The buffer is too small\n");
66+ kfree(obj);
67+ return 0;
68+ }
69+
70+ freq = *(u32 *)(obj->buffer.pointer + CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET);
71+
72+ kfree(obj);
73+ return freq;
74+}
75+
76+static unsigned long skl_int3472_clk_recalc_rate(struct clk_hw *hw,
77+ unsigned long parent_rate)
78+{
79+ struct int3472_gpio_clock *clk = to_int3472_clk(hw);
80+
81+ return clk->frequency;
82+}
83+
84+static const struct clk_ops skl_int3472_clock_ops = {
85+ .prepare = skl_int3472_clk_prepare,
86+ .unprepare = skl_int3472_clk_unprepare,
87+ .enable = skl_int3472_clk_enable,
88+ .disable = skl_int3472_clk_disable,
89+ .recalc_rate = skl_int3472_clk_recalc_rate,
90+};
91+
92+int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
93+{
94+ struct clk_init_data init = {
95+ .ops = &skl_int3472_clock_ops,
96+ .flags = CLK_GET_RATE_NOCACHE,
97+ };
98+ int ret;
99+
100+ init.name = kasprintf(GFP_KERNEL, "%s-clk",
101+ acpi_dev_name(int3472->adev));
102+ if (!init.name)
103+ return -ENOMEM;
104+
105+ int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472);
106+
107+ int3472->clock.clk_hw.init = &init;
108+ int3472->clock.clk = clk_register(&int3472->adev->dev,
109+ &int3472->clock.clk_hw);
110+ if (IS_ERR(int3472->clock.clk)) {
111+ ret = PTR_ERR(int3472->clock.clk);
112+ goto out_free_init_name;
113+ }
114+
115+ int3472->clock.cl = clkdev_create(int3472->clock.clk, NULL,
116+ int3472->sensor_name);
117+ if (!int3472->clock.cl) {
118+ ret = -ENOMEM;
119+ goto err_unregister_clk;
120+ }
121+
122+ kfree(init.name);
123+ return 0;
124+
125+err_unregister_clk:
126+ clk_unregister(int3472->clock.clk);
127+out_free_init_name:
128+ kfree(init.name);
129+
130+ return ret;
131+}
132+
133+void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)
134+{
135+ clkdev_drop(int3472->clock.cl);
136+ clk_unregister(int3472->clock.clk);
137+}
138+
139+int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
140+ struct acpi_resource_gpio *agpio)
141+{
142+ const struct int3472_sensor_config *sensor_config;
143+ char *path = agpio->resource_source.string_ptr;
144+ struct regulator_consumer_supply supply_map;
145+ struct regulator_init_data init_data = { };
146+ struct regulator_config cfg = { };
147+ int ret;
148+
149+ sensor_config = int3472->sensor_config;
150+ if (IS_ERR(sensor_config)) {
151+ dev_err(int3472->dev, "No sensor module config\n");
152+ return PTR_ERR(sensor_config);
153+ }
154+
155+ if (!sensor_config->supply_map.supply) {
156+ dev_err(int3472->dev, "No supply name defined\n");
157+ return -ENODEV;
158+ }
159+
160+ init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
161+ init_data.num_consumer_supplies = 1;
162+ supply_map = sensor_config->supply_map;
163+ supply_map.dev_name = int3472->sensor_name;
164+ init_data.consumer_supplies = &supply_map;
165+
166+ snprintf(int3472->regulator.regulator_name,
167+ sizeof(int3472->regulator.regulator_name), "%s-regulator",
168+ acpi_dev_name(int3472->adev));
169+ snprintf(int3472->regulator.supply_name,
170+ GPIO_REGULATOR_SUPPLY_NAME_LENGTH, "supply-0");
171+
172+ int3472->regulator.rdesc = INT3472_REGULATOR(
173+ int3472->regulator.regulator_name,
174+ int3472->regulator.supply_name,
175+ &int3472_gpio_regulator_ops);
176+
177+ int3472->regulator.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0],
178+ "int3472,regulator");
179+ if (IS_ERR(int3472->regulator.gpio)) {
180+ dev_err(int3472->dev, "Failed to get regulator GPIO line\n");
181+ return PTR_ERR(int3472->regulator.gpio);
182+ }
183+
184+ cfg.dev = &int3472->adev->dev;
185+ cfg.init_data = &init_data;
186+ cfg.ena_gpiod = int3472->regulator.gpio;
187+
188+ int3472->regulator.rdev = regulator_register(&int3472->regulator.rdesc,
189+ &cfg);
190+ if (IS_ERR(int3472->regulator.rdev)) {
191+ ret = PTR_ERR(int3472->regulator.rdev);
192+ goto err_free_gpio;
193+ }
194+
195+ return 0;
196+
197+err_free_gpio:
198+ gpiod_put(int3472->regulator.gpio);
199+
200+ return ret;
201+}
202+
203+void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472)
204+{
205+ regulator_unregister(int3472->regulator.rdev);
206+ gpiod_put(int3472->regulator.gpio);
207+}
drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c+106−0
@@ -0,0 +1,106 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/* Author: Dan Scally <djrscally@gmail.com> */
3+
4+#include <linux/acpi.h>
5+#include <linux/i2c.h>
6+#include <linux/platform_device.h>
7+#include <linux/slab.h>
8+
9+#include "intel_skl_int3472_common.h"
10+
11+union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id)
12+{
13+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
14+ acpi_handle handle = adev->handle;
15+ union acpi_object *obj;
16+ acpi_status status;
17+
18+ status = acpi_evaluate_object(handle, id, NULL, &buffer);
19+ if (ACPI_FAILURE(status))
20+ return ERR_PTR(-ENODEV);
21+
22+ obj = buffer.pointer;
23+ if (!obj)
24+ return ERR_PTR(-ENODEV);
25+
26+ if (obj->type != ACPI_TYPE_BUFFER) {
27+ acpi_handle_err(handle, "%s object is not an ACPI buffer\n", id);
28+ kfree(obj);
29+ return ERR_PTR(-EINVAL);
30+ }
31+
32+ return obj;
33+}
34+
35+int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb)
36+{
37+ union acpi_object *obj;
38+ int ret;
39+
40+ obj = skl_int3472_get_acpi_buffer(adev, "CLDB");
41+ if (IS_ERR(obj))
42+ return PTR_ERR(obj);
43+
44+ if (obj->buffer.length > sizeof(*cldb)) {
45+ acpi_handle_err(adev->handle, "The CLDB buffer is too large\n");
46+ ret = -EINVAL;
47+ goto out_free_obj;
48+ }
49+
50+ memcpy(cldb, obj->buffer.pointer, obj->buffer.length);
51+ ret = 0;
52+
53+out_free_obj:
54+ kfree(obj);
55+ return ret;
56+}
57+
58+static const struct acpi_device_id int3472_device_id[] = {
59+ { "INT3472", 0 },
60+ { }
61+};
62+MODULE_DEVICE_TABLE(acpi, int3472_device_id);
63+
64+static struct platform_driver int3472_discrete = {
65+ .driver = {
66+ .name = "int3472-discrete",
67+ .acpi_match_table = int3472_device_id,
68+ },
69+ .probe = skl_int3472_discrete_probe,
70+ .remove = skl_int3472_discrete_remove,
71+};
72+
73+static struct i2c_driver int3472_tps68470 = {
74+ .driver = {
75+ .name = "int3472-tps68470",
76+ .acpi_match_table = int3472_device_id,
77+ },
78+ .probe_new = skl_int3472_tps68470_probe,
79+};
80+
81+static int skl_int3472_init(void)
82+{
83+ int ret;
84+
85+ ret = platform_driver_register(&int3472_discrete);
86+ if (ret)
87+ return ret;
88+
89+ ret = i2c_register_driver(THIS_MODULE, &int3472_tps68470);
90+ if (ret)
91+ platform_driver_unregister(&int3472_discrete);
92+
93+ return ret;
94+}
95+module_init(skl_int3472_init);
96+
97+static void skl_int3472_exit(void)
98+{
99+ platform_driver_unregister(&int3472_discrete);
100+ i2c_del_driver(&int3472_tps68470);
101+}
102+module_exit(skl_int3472_exit);
103+
104+MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver");
105+MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
106+MODULE_LICENSE("GPL v2");
drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h+122−0
@@ -0,0 +1,122 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/* Author: Dan Scally <djrscally@gmail.com> */
3+
4+#ifndef _INTEL_SKL_INT3472_H
5+#define _INTEL_SKL_INT3472_H
6+
7+#include <linux/clk-provider.h>
8+#include <linux/gpio/machine.h>
9+#include <linux/regulator/driver.h>
10+#include <linux/regulator/machine.h>
11+#include <linux/types.h>
12+
13+/* FIXME drop this once the I2C_DEV_NAME_FORMAT macro has been added to include/linux/i2c.h */
14+#ifndef I2C_DEV_NAME_FORMAT
15+#define I2C_DEV_NAME_FORMAT "i2c-%s"
16+#endif
17+
18+/* PMIC GPIO Types */
19+#define INT3472_GPIO_TYPE_RESET 0x00
20+#define INT3472_GPIO_TYPE_POWERDOWN 0x01
21+#define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b
22+#define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c
23+#define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d
24+
25+#define INT3472_PDEV_MAX_NAME_LEN 23
26+#define INT3472_MAX_SENSOR_GPIOS 3
27+
28+#define GPIO_REGULATOR_NAME_LENGTH 21
29+#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
30+
31+#define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86
32+
33+#define INT3472_REGULATOR(_name, _supply, _ops) \
34+ (const struct regulator_desc) { \
35+ .name = _name, \
36+ .supply_name = _supply, \
37+ .type = REGULATOR_VOLTAGE, \
38+ .ops = _ops, \
39+ .owner = THIS_MODULE, \
40+ }
41+
42+#define to_int3472_clk(hw) \
43+ container_of(hw, struct int3472_gpio_clock, clk_hw)
44+
45+#define to_int3472_device(clk) \
46+ container_of(clk, struct int3472_discrete_device, clock)
47+
48+struct acpi_device;
49+struct i2c_client;
50+struct platform_device;
51+
52+struct int3472_cldb {
53+ u8 version;
54+ /*
55+ * control logic type
56+ * 0: UNKNOWN
57+ * 1: DISCRETE(CRD-D)
58+ * 2: PMIC TPS68470
59+ * 3: PMIC uP6641
60+ */
61+ u8 control_logic_type;
62+ u8 control_logic_id;
63+ u8 sensor_card_sku;
64+ u8 reserved[28];
65+};
66+
67+struct int3472_gpio_function_remap {
68+ const char *documented;
69+ const char *actual;
70+};
71+
72+struct int3472_sensor_config {
73+ const char *sensor_module_name;
74+ struct regulator_consumer_supply supply_map;
75+ const struct int3472_gpio_function_remap *function_maps;
76+};
77+
78+struct int3472_discrete_device {
79+ struct acpi_device *adev;
80+ struct device *dev;
81+ struct acpi_device *sensor;
82+ const char *sensor_name;
83+
84+ const struct int3472_sensor_config *sensor_config;
85+
86+ struct int3472_gpio_regulator {
87+ char regulator_name[GPIO_REGULATOR_NAME_LENGTH];
88+ char supply_name[GPIO_REGULATOR_SUPPLY_NAME_LENGTH];
89+ struct gpio_desc *gpio;
90+ struct regulator_dev *rdev;
91+ struct regulator_desc rdesc;
92+ } regulator;
93+
94+ struct int3472_gpio_clock {
95+ struct clk *clk;
96+ struct clk_hw clk_hw;
97+ struct clk_lookup *cl;
98+ struct gpio_desc *ena_gpio;
99+ struct gpio_desc *led_gpio;
100+ u32 frequency;
101+ } clock;
102+
103+ unsigned int ngpios; /* how many GPIOs have we seen */
104+ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */
105+ struct gpiod_lookup_table gpios;
106+};
107+
108+int skl_int3472_discrete_probe(struct platform_device *pdev);
109+int skl_int3472_discrete_remove(struct platform_device *pdev);
110+int skl_int3472_tps68470_probe(struct i2c_client *client);
111+union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev,
112+ char *id);
113+int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb);
114+
115+int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
116+void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
117+
118+int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
119+ struct acpi_resource_gpio *agpio);
120+void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472);
121+
122+#endif
drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c+413−0
@@ -0,0 +1,413 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/* Author: Dan Scally <djrscally@gmail.com> */
3+
4+#include <linux/acpi.h>
5+#include <linux/clkdev.h>
6+#include <linux/clk-provider.h>
7+#include <linux/device.h>
8+#include <linux/gpio/consumer.h>
9+#include <linux/gpio/machine.h>
10+#include <linux/i2c.h>
11+#include <linux/kernel.h>
12+#include <linux/module.h>
13+#include <linux/overflow.h>
14+#include <linux/platform_device.h>
15+#include <linux/uuid.h>
16+
17+#include "intel_skl_int3472_common.h"
18+
19+/*
20+ * 79234640-9e10-4fea-a5c1-b5aa8b19756f
21+ * This _DSM GUID returns information about the GPIO lines mapped to a
22+ * discrete INT3472 device. Function number 1 returns a count of the GPIO
23+ * lines that are mapped. Subsequent functions return 32 bit ints encoding
24+ * information about the GPIO line, including its purpose.
25+ */
26+static const guid_t int3472_gpio_guid =
27+ GUID_INIT(0x79234640, 0x9e10, 0x4fea,
28+ 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
29+
30+/*
31+ * 822ace8f-2814-4174-a56b-5f029fe079ee
32+ * This _DSM GUID returns a string from the sensor device, which acts as a
33+ * module identifier.
34+ */
35+static const guid_t cio2_sensor_module_guid =
36+ GUID_INIT(0x822ace8f, 0x2814, 0x4174,
37+ 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
38+
39+/*
40+ * Here follows platform specific mapping information that we can pass to
41+ * the functions mapping resources to the sensors. Where the sensors have
42+ * a power enable pin defined in DSDT we need to provide a supply name so
43+ * the sensor drivers can find the regulator. The device name will be derived
44+ * from the sensor's ACPI device within the code. Optionally, we can provide a
45+ * NULL terminated array of function name mappings to deal with any platform
46+ * specific deviations from the documented behaviour of GPIOs.
47+ *
48+ * Map a GPIO function name to NULL to prevent the driver from mapping that
49+ * GPIO at all.
50+ */
51+
52+static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = {
53+ { "reset", NULL },
54+ { "powerdown", "reset" },
55+ { }
56+};
57+
58+static const struct int3472_sensor_config int3472_sensor_configs[] = {
59+ /* Lenovo Miix 510-12ISK - OV2680, Front */
60+ { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps },
61+ /* Lenovo Miix 510-12ISK - OV5648, Rear */
62+ { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL },
63+ /* Surface Go 1&2 - OV5693, Front */
64+ { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL },
65+};
66+
67+static const struct int3472_sensor_config *
68+skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472)
69+{
70+ union acpi_object *obj;
71+ unsigned int i;
72+
73+ obj = acpi_evaluate_dsm_typed(int3472->sensor->handle,
74+ &cio2_sensor_module_guid, 0x00,
75+ 0x01, NULL, ACPI_TYPE_STRING);
76+
77+ if (!obj) {
78+ dev_err(int3472->dev,
79+ "Failed to get sensor module string from _DSM\n");
80+ return ERR_PTR(-ENODEV);
81+ }
82+
83+ if (obj->string.type != ACPI_TYPE_STRING) {
84+ dev_err(int3472->dev,
85+ "Sensor _DSM returned a non-string value\n");
86+
87+ ACPI_FREE(obj);
88+ return ERR_PTR(-EINVAL);
89+ }
90+
91+ for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) {
92+ if (!strcmp(int3472_sensor_configs[i].sensor_module_name,
93+ obj->string.pointer))
94+ break;
95+ }
96+
97+ ACPI_FREE(obj);
98+
99+ if (i >= ARRAY_SIZE(int3472_sensor_configs))
100+ return ERR_PTR(-EINVAL);
101+
102+ return &int3472_sensor_configs[i];
103+}
104+
105+static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
106+ struct acpi_resource_gpio *agpio,
107+ const char *func, u32 polarity)
108+{
109+ const struct int3472_sensor_config *sensor_config;
110+ char *path = agpio->resource_source.string_ptr;
111+ struct gpiod_lookup *table_entry;
112+ struct acpi_device *adev;
113+ acpi_handle handle;
114+ acpi_status status;
115+ int ret;
116+
117+ if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
118+ dev_warn(int3472->dev, "Too many GPIOs mapped\n");
119+ return -EINVAL;
120+ }
121+
122+ sensor_config = int3472->sensor_config;
123+ if (!IS_ERR(sensor_config) && sensor_config->function_maps) {
124+ const struct int3472_gpio_function_remap *remap;
125+
126+ for (remap = sensor_config->function_maps; remap->documented; remap++) {
127+ if (!strcmp(func, remap->documented)) {
128+ func = remap->actual;
129+ break;
130+ }
131+ }
132+ }
133+
134+ /* Functions mapped to NULL should not be mapped to the sensor */
135+ if (!func)
136+ return 0;
137+
138+ status = acpi_get_handle(NULL, path, &handle);
139+ if (ACPI_FAILURE(status))
140+ return -EINVAL;
141+
142+ ret = acpi_bus_get_device(handle, &adev);
143+ if (ret)
144+ return -ENODEV;
145+
146+ table_entry = &int3472->gpios.table[int3472->n_sensor_gpios];
147+ table_entry->key = acpi_dev_name(adev);
148+ table_entry->chip_hwnum = agpio->pin_table[0];
149+ table_entry->con_id = func;
150+ table_entry->idx = 0;
151+ table_entry->flags = polarity;
152+
153+ int3472->n_sensor_gpios++;
154+
155+ return 0;
156+}
157+
158+static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
159+ struct acpi_resource_gpio *agpio, u8 type)
160+{
161+ char *path = agpio->resource_source.string_ptr;
162+ u16 pin = agpio->pin_table[0];
163+ struct gpio_desc *gpio;
164+
165+ switch (type) {
166+ case INT3472_GPIO_TYPE_CLK_ENABLE:
167+ gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable");
168+ if (IS_ERR(gpio))
169+ return (PTR_ERR(gpio));
170+
171+ int3472->clock.ena_gpio = gpio;
172+ break;
173+ case INT3472_GPIO_TYPE_PRIVACY_LED:
174+ gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led");
175+ if (IS_ERR(gpio))
176+ return (PTR_ERR(gpio));
177+
178+ int3472->clock.led_gpio = gpio;
179+ break;
180+ default:
181+ dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type);
182+ break;
183+ }
184+
185+ return 0;
186+}
187+
188+/**
189+ * skl_int3472_handle_gpio_resources: Map PMIC resources to consuming sensor
190+ * @ares: A pointer to a &struct acpi_resource
191+ * @data: A pointer to a &struct int3472_discrete_device
192+ *
193+ * This function handles GPIO resources that are against an INT3472
194+ * ACPI device, by checking the value of the corresponding _DSM entry.
195+ * This will return a 32bit int, where the lowest byte represents the
196+ * function of the GPIO pin:
197+ *
198+ * 0x00 Reset
199+ * 0x01 Power down
200+ * 0x0b Power enable
201+ * 0x0c Clock enable
202+ * 0x0d Privacy LED
203+ *
204+ * There are some known platform specific quirks where that does not quite
205+ * hold up; for example where a pin with type 0x01 (Power down) is mapped to
206+ * a sensor pin that performs a reset function or entries in _CRS and _DSM that
207+ * do not actually correspond to a physical connection. These will be handled
208+ * by the mapping sub-functions.
209+ *
210+ * GPIOs will either be mapped directly to the sensor device or else used
211+ * to create clocks and regulators via the usual frameworks.
212+ *
213+ * Return:
214+ * * 1 - To continue the loop
215+ * * 0 - When all resources found are handled properly.
216+ * * -EINVAL - If the resource is not a GPIO IO resource
217+ * * -ENODEV - If the resource has no corresponding _DSM entry
218+ * * -Other - Errors propagated from one of the sub-functions.
219+ */
220+static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
221+ void *data)
222+{
223+ struct int3472_discrete_device *int3472 = data;
224+ struct acpi_resource_gpio *agpio;
225+ union acpi_object *obj;
226+ const char *err_msg;
227+ int ret;
228+ u8 type;
229+
230+ if (!acpi_gpio_get_io_resource(ares, &agpio))
231+ return 1;
232+
233+ /*
234+ * ngpios + 2 because the index of this _DSM function is 1-based and
235+ * the first function is just a count.
236+ */
237+ obj = acpi_evaluate_dsm_typed(int3472->adev->handle,
238+ &int3472_gpio_guid, 0x00,
239+ int3472->ngpios + 2,
240+ NULL, ACPI_TYPE_INTEGER);
241+
242+ if (!obj) {
243+ dev_warn(int3472->dev, "No _DSM entry for GPIO pin %u\n",
244+ agpio->pin_table[0]);
245+ return 1;
246+ }
247+
248+ type = obj->integer.value & 0xff;
249+
250+ switch (type) {
251+ case INT3472_GPIO_TYPE_RESET:
252+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset",
253+ GPIO_ACTIVE_LOW);
254+ if (ret)
255+ err_msg = "Failed to map reset pin to sensor\n";
256+
257+ break;
258+ case INT3472_GPIO_TYPE_POWERDOWN:
259+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown",
260+ GPIO_ACTIVE_LOW);
261+ if (ret)
262+ err_msg = "Failed to map powerdown pin to sensor\n";
263+
264+ break;
265+ case INT3472_GPIO_TYPE_CLK_ENABLE:
266+ case INT3472_GPIO_TYPE_PRIVACY_LED:
267+ ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type);
268+ if (ret)
269+ err_msg = "Failed to map GPIO to clock\n";
270+
271+ break;
272+ case INT3472_GPIO_TYPE_POWER_ENABLE:
273+ ret = skl_int3472_register_regulator(int3472, agpio);
274+ if (ret)
275+ err_msg = "Failed to map regulator to sensor\n";
276+
277+ break;
278+ default:
279+ dev_warn(int3472->dev,
280+ "GPIO type 0x%02x unknown; the sensor may not work\n",
281+ type);
282+ ret = 1;
283+ break;
284+ }
285+
286+ int3472->ngpios++;
287+ ACPI_FREE(obj);
288+
289+ if (ret < 0)
290+ return dev_err_probe(int3472->dev, ret, err_msg);
291+
292+ return ret;
293+}
294+
295+static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
296+{
297+ LIST_HEAD(resource_list);
298+ int ret;
299+
300+ /*
301+ * No error check, because not having a sensor config is not necessarily
302+ * a failure mode.
303+ */
304+ int3472->sensor_config = skl_int3472_get_sensor_module_config(int3472);
305+
306+ ret = acpi_dev_get_resources(int3472->adev, &resource_list,
307+ skl_int3472_handle_gpio_resources,
308+ int3472);
309+ if (ret < 0)
310+ return ret;
311+
312+ acpi_dev_free_resource_list(&resource_list);
313+
314+ /*
315+ * If we find no clock enable GPIO pin then the privacy LED won't work.
316+ * We've never seen that situation, but it's possible. Warn the user so
317+ * it's clear what's happened.
318+ */
319+ if (int3472->clock.ena_gpio) {
320+ ret = skl_int3472_register_clock(int3472);
321+ if (ret)
322+ return ret;
323+ } else {
324+ if (int3472->clock.led_gpio)
325+ dev_warn(int3472->dev,
326+ "No clk GPIO. The privacy LED won't work\n");
327+ }
328+
329+ int3472->gpios.dev_id = int3472->sensor_name;
330+ gpiod_add_lookup_table(&int3472->gpios);
331+
332+ return 0;
333+}
334+
335+int skl_int3472_discrete_probe(struct platform_device *pdev)
336+{
337+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
338+ struct int3472_discrete_device *int3472;
339+ struct int3472_cldb cldb;
340+ int ret;
341+
342+ ret = skl_int3472_fill_cldb(adev, &cldb);
343+ if (ret) {
344+ dev_err(&pdev->dev, "Couldn't fill CLDB structure\n");
345+ return ret;
346+ }
347+
348+ if (cldb.control_logic_type != 1) {
349+ dev_err(&pdev->dev, "Unsupported control logic type %u\n",
350+ cldb.control_logic_type);
351+ return -EINVAL;
352+ }
353+
354+ /* Max num GPIOs we've seen plus a terminator */
355+ int3472 = devm_kzalloc(&pdev->dev, struct_size(int3472, gpios.table,
356+ INT3472_MAX_SENSOR_GPIOS + 1), GFP_KERNEL);
357+ if (!int3472)
358+ return -ENOMEM;
359+
360+ int3472->adev = adev;
361+ int3472->dev = &pdev->dev;
362+ platform_set_drvdata(pdev, int3472);
363+
364+ int3472->sensor = acpi_dev_get_first_consumer_dev(adev);
365+ if (!int3472->sensor) {
366+ dev_err(&pdev->dev, "INT3472 seems to have no dependents.\n");
367+ return -ENODEV;
368+ }
369+
370+ int3472->sensor_name = devm_kasprintf(int3472->dev, GFP_KERNEL,
371+ I2C_DEV_NAME_FORMAT,
372+ acpi_dev_name(int3472->sensor));
373+ if (!int3472->sensor_name) {
374+ ret = -ENOMEM;
375+ goto err_put_sensor;
376+ }
377+
378+ /*
379+ * Initialising this list means we can call gpiod_remove_lookup_table()
380+ * in failure paths without issue.
381+ */
382+ INIT_LIST_HEAD(&int3472->gpios.list);
383+
384+ ret = skl_int3472_parse_crs(int3472);
385+ if (ret) {
386+ skl_int3472_discrete_remove(pdev);
387+ return ret;
388+ }
389+
390+ return 0;
391+
392+err_put_sensor:
393+ acpi_dev_put(int3472->sensor);
394+
395+ return ret;
396+}
397+
398+int skl_int3472_discrete_remove(struct platform_device *pdev)
399+{
400+ struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev);
401+
402+ gpiod_remove_lookup_table(&int3472->gpios);
403+
404+ if (int3472->clock.ena_gpio)
405+ skl_int3472_unregister_clock(int3472);
406+
407+ gpiod_put(int3472->clock.ena_gpio);
408+ gpiod_put(int3472->clock.led_gpio);
409+
410+ skl_int3472_unregister_regulator(int3472);
411+
412+ return 0;
413+}
drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c+137−0
@@ -0,0 +1,137 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/* Author: Dan Scally <djrscally@gmail.com> */
3+
4+#include <linux/i2c.h>
5+#include <linux/mfd/core.h>
6+#include <linux/mfd/tps68470.h>
7+#include <linux/platform_device.h>
8+#include <linux/regmap.h>
9+
10+#include "intel_skl_int3472_common.h"
11+
12+#define DESIGNED_FOR_CHROMEOS 1
13+#define DESIGNED_FOR_WINDOWS 2
14+
15+static const struct mfd_cell tps68470_cros[] = {
16+ { .name = "tps68470-gpio" },
17+ { .name = "tps68470_pmic_opregion" },
18+};
19+
20+static const struct mfd_cell tps68470_win[] = {
21+ { .name = "tps68470-gpio" },
22+ { .name = "tps68470-clk" },
23+ { .name = "tps68470-regulator" },
24+};
25+
26+static const struct regmap_config tps68470_regmap_config = {
27+ .reg_bits = 8,
28+ .val_bits = 8,
29+ .max_register = TPS68470_REG_MAX,
30+};
31+
32+static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
33+{
34+ unsigned int version;
35+ int ret;
36+
37+ /* Force software reset */
38+ ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
39+ if (ret)
40+ return ret;
41+
42+ ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
43+ if (ret) {
44+ dev_err(dev, "Failed to read revision register: %d\n", ret);
45+ return ret;
46+ }
47+
48+ dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
49+
50+ return 0;
51+}
52+
53+/** skl_int3472_tps68470_calc_type: Check what platform a device is designed for
54+ * @adev: A pointer to a &struct acpi_device
55+ *
56+ * Check CLDB buffer against the PMIC's adev. If present, then we check
57+ * the value of control_logic_type field and follow one of the
58+ * following scenarios:
59+ *
60+ * 1. No CLDB - likely ACPI tables designed for ChromeOS. We
61+ * create platform devices for the GPIOs and OpRegion drivers.
62+ *
63+ * 2. CLDB, with control_logic_type = 2 - probably ACPI tables
64+ * made for Windows 2-in-1 platforms. Register pdevs for GPIO,
65+ * Clock and Regulator drivers to bind to.
66+ *
67+ * 3. Any other value in control_logic_type, we should never have
68+ * gotten to this point; fail probe and return.
69+ *
70+ * Return:
71+ * * 1 Device intended for ChromeOS
72+ * * 2 Device intended for Windows
73+ * * -EINVAL Where @adev has an object named CLDB but it does not conform to
74+ * our expectations
75+ */
76+static int skl_int3472_tps68470_calc_type(struct acpi_device *adev)
77+{
78+ struct int3472_cldb cldb = { 0 };
79+ int ret;
80+
81+ /*
82+ * A CLDB buffer that exists, but which does not match our expectations
83+ * should trigger an error so we don't blindly continue.
84+ */
85+ ret = skl_int3472_fill_cldb(adev, &cldb);
86+ if (ret && ret != -ENODEV)
87+ return ret;
88+
89+ if (ret)
90+ return DESIGNED_FOR_CHROMEOS;
91+
92+ if (cldb.control_logic_type != 2)
93+ return -EINVAL;
94+
95+ return DESIGNED_FOR_WINDOWS;
96+}
97+
98+int skl_int3472_tps68470_probe(struct i2c_client *client)
99+{
100+ struct acpi_device *adev = ACPI_COMPANION(&client->dev);
101+ struct regmap *regmap;
102+ int device_type;
103+ int ret;
104+
105+ regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
106+ if (IS_ERR(regmap)) {
107+ dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap));
108+ return PTR_ERR(regmap);
109+ }
110+
111+ i2c_set_clientdata(client, regmap);
112+
113+ ret = tps68470_chip_init(&client->dev, regmap);
114+ if (ret < 0) {
115+ dev_err(&client->dev, "TPS68470 init error %d\n", ret);
116+ return ret;
117+ }
118+
119+ device_type = skl_int3472_tps68470_calc_type(adev);
120+ switch (device_type) {
121+ case DESIGNED_FOR_WINDOWS:
122+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
123+ tps68470_win, ARRAY_SIZE(tps68470_win),
124+ NULL, 0, NULL);
125+ break;
126+ case DESIGNED_FOR_CHROMEOS:
127+ ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
128+ tps68470_cros, ARRAY_SIZE(tps68470_cros),
129+ NULL, 0, NULL);
130+ break;
131+ default:
132+ dev_err(&client->dev, "Failed to add MFD devices\n");
133+ return device_type;
134+ }
135+
136+ return ret;
137+}
drivers/platform/x86/intel_ips.c+1−1
@@ -829,7 +829,7 @@ static u16 calc_avg_temp(struct ips_driver *ips, u16 *array) · 828 unmodified lines
829829
830830 static u16 read_mgtv(struct ips_driver *ips)
831831 {
832− u16 ret;
832+ u16 __maybe_unused ret;
833833 u64 slope, offset;
834834 u64 val;
835835
drivers/platform/x86/intel_pmt_crashlog.c+1−1
@@ -218,7 +218,7 @@ static struct attribute *pmt_crashlog_attrs[] = { · 217 unmodified lines
218218 NULL
219219 };
220220
221−static struct attribute_group pmt_crashlog_group = {
221+static const struct attribute_group pmt_crashlog_group = {
222222 .attrs = pmt_crashlog_attrs,
223223 };
224224
drivers/platform/x86/intel_speed_select_if/isst_if_common.c+68−5
@@ -281,10 +281,69 @@ static int isst_if_get_platform_info(void __user *argp) · 280 unmodified lines
281281 struct isst_if_cpu_info {
282282 /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
283283 int bus_info[2];
284+ struct pci_dev *pci_dev[2];
284285 int punit_cpu_id;
286+ int numa_node;
285287 };
286288
287289 static struct isst_if_cpu_info *isst_cpu_info;
290+#define ISST_MAX_PCI_DOMAINS 8
291+
292+static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
293+{
294+ struct pci_dev *matched_pci_dev = NULL;
295+ struct pci_dev *pci_dev = NULL;
296+ int no_matches = 0;
297+ int i, bus_number;
298+
299+ if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
300+ cpu >= num_possible_cpus())
301+ return NULL;
302+
303+ bus_number = isst_cpu_info[cpu].bus_info[bus_no];
304+ if (bus_number < 0)
305+ return NULL;
306+
307+ for (i = 0; i < ISST_MAX_PCI_DOMAINS; ++i) {
308+ struct pci_dev *_pci_dev;
309+ int node;
310+
311+ _pci_dev = pci_get_domain_bus_and_slot(i, bus_number, PCI_DEVFN(dev, fn));
312+ if (!_pci_dev)
313+ continue;
314+
315+ ++no_matches;
316+ if (!matched_pci_dev)
317+ matched_pci_dev = _pci_dev;
318+
319+ node = dev_to_node(&_pci_dev->dev);
320+ if (node == NUMA_NO_NODE) {
321+ pr_info("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n",
322+ cpu, bus_no, dev, fn);
323+ continue;
324+ }
325+
326+ if (node == isst_cpu_info[cpu].numa_node) {
327+ pci_dev = _pci_dev;
328+ break;
329+ }
330+ }
331+
332+ /*
333+ * If there is no numa matched pci_dev, then there can be following cases:
334+ * 1. CONFIG_NUMA is not defined: In this case if there is only single device
335+ * match, then we don't need numa information. Simply return last match.
336+ * Othewise return NULL.
337+ * 2. NUMA information is not exposed via _SEG method. In this case it is similar
338+ * to case 1.
339+ * 3. Numa information doesn't match with CPU numa node and more than one match
340+ * return NULL.
341+ */
342+ if (!pci_dev && no_matches == 1)
343+ pci_dev = matched_pci_dev;
344+
345+ return pci_dev;
346+}
288347
289348 /**
290349 * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
@@ -300,17 +359,18 @@ static struct isst_if_cpu_info *isst_cpu_info; · 9 unmodified lines
300359 */
301360 struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
302361 {
303− int bus_number;
362+ struct pci_dev *pci_dev;
304363
305364 if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
306365 cpu >= num_possible_cpus())
307366 return NULL;
308367
309− bus_number = isst_cpu_info[cpu].bus_info[bus_no];
310− if (bus_number < 0)
311− return NULL;
368+ pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
369+
370+ if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn))
371+ return pci_dev;
312372
313− return pci_get_domain_bus_and_slot(0, bus_number, PCI_DEVFN(dev, fn));
373+ return _isst_if_get_pci_dev(cpu, bus_no, dev, fn);
314374 }
315375 EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
316376
@@ -327,6 +387,8 @@ static int isst_if_cpu_online(unsigned int cpu) · 10 unmodified lines
327387 } else {
328388 isst_cpu_info[cpu].bus_info[0] = data & 0xff;
329389 isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
390+ isst_cpu_info[cpu].pci_dev[0] = _isst_if_get_pci_dev(cpu, 0, 0, 1);
391+ isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
330392 }
331393
332394 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
@@ -335,6 +397,7 @@ static int isst_if_cpu_online(unsigned int cpu) · 2 unmodified lines
335397 return ret;
336398 }
337399 isst_cpu_info[cpu].punit_cpu_id = data;
400+ isst_cpu_info[cpu].numa_node = cpu_to_node(cpu);
338401
339402 isst_restore_msr_local(cpu);
340403
drivers/platform/x86/samsung-laptop.c+14−21
@@ -388,7 +388,7 @@ MODULE_PARM_DESC(force, · 387 unmodified lines
388388 "Disable the DMI check and forces the driver to be loaded");
389389
390390 static bool debug;
391−module_param(debug, bool, S_IRUGO | S_IWUSR);
391+module_param(debug, bool, 0644);
392392 MODULE_PARM_DESC(debug, "Debug enabled or not");
393393
394394 static int sabi_command(struct samsung_laptop *samsung, u16 command,
@@ -705,7 +705,7 @@ static ssize_t set_performance_level(struct device *dev, · 310 unmodified lines
705705 return count;
706706 }
707707
708−static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
708+static DEVICE_ATTR(performance_level, 0644,
709709 get_performance_level, set_performance_level);
710710
711711 static int read_battery_life_extender(struct samsung_laptop *samsung)
@@ -774,7 +774,7 @@ static ssize_t set_battery_life_extender(struct device *dev, · 62 unmodified lines
774774 return count;
775775 }
776776
777−static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
777+static DEVICE_ATTR(battery_life_extender, 0644,
778778 get_battery_life_extender, set_battery_life_extender);
779779
780780 static int read_usb_charge(struct samsung_laptop *samsung)
@@ -843,7 +843,7 @@ static ssize_t set_usb_charge(struct device *dev, · 62 unmodified lines
843843 return count;
844844 }
845845
846−static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
846+static DEVICE_ATTR(usb_charge, 0644,
847847 get_usb_charge, set_usb_charge);
848848
849849 static int read_lid_handling(struct samsung_laptop *samsung)
@@ -908,7 +908,7 @@ static ssize_t set_lid_handling(struct device *dev, · 58 unmodified lines
908908 return count;
909909 }
910910
911−static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO,
911+static DEVICE_ATTR(lid_handling, 0644,
912912 get_lid_handling, set_lid_handling);
913913
914914 static struct attribute *platform_attributes[] = {
@@ -1291,24 +1291,17 @@ static void samsung_debugfs_init(struct samsung_laptop *samsung) · 376 unmodified lines
12911291 samsung->debug.sdiag_wrapper.data = samsung->sdiag;
12921292 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
12931293
1294− debugfs_create_u16("command", S_IRUGO | S_IWUSR, root,
1295− &samsung->debug.command);
1296− debugfs_create_u32("d0", S_IRUGO | S_IWUSR, root,
1297− &samsung->debug.data.d0);
1298− debugfs_create_u32("d1", S_IRUGO | S_IWUSR, root,
1299− &samsung->debug.data.d1);
1300− debugfs_create_u16("d2", S_IRUGO | S_IWUSR, root,
1301− &samsung->debug.data.d2);
1302− debugfs_create_u8("d3", S_IRUGO | S_IWUSR, root,
1303− &samsung->debug.data.d3);
1304− debugfs_create_blob("data", S_IRUGO | S_IWUSR, root,
1305− &samsung->debug.data_wrapper);
1306− debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, root,
1294+ debugfs_create_u16("command", 0644, root, &samsung->debug.command);
1295+ debugfs_create_u32("d0", 0644, root, &samsung->debug.data.d0);
1296+ debugfs_create_u32("d1", 0644, root, &samsung->debug.data.d1);
1297+ debugfs_create_u16("d2", 0644, root, &samsung->debug.data.d2);
1298+ debugfs_create_u8("d3", 0644, root, &samsung->debug.data.d3);
1299+ debugfs_create_blob("data", 0444, root, &samsung->debug.data_wrapper);
1300+ debugfs_create_blob("f0000_segment", 0400, root,
13071301 &samsung->debug.f0000_wrapper);
1308− debugfs_create_file("call", S_IFREG | S_IRUGO, root, samsung,
1302+ debugfs_create_file("call", 0444, root, samsung,
13091303 &samsung_laptop_call_fops);
1310− debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, root,
1311− &samsung->debug.sdiag_wrapper);
1304+ debugfs_create_blob("sdiag", 0444, root, &samsung->debug.sdiag_wrapper);
13121305 }
13131306
13141307 static void samsung_sabi_exit(struct samsung_laptop *samsung)
drivers/platform/x86/tc1100-wmi.c+1−1
@@ -156,7 +156,7 @@ static struct attribute *tc1100_attributes[] = { · 155 unmodified lines
156156 NULL
157157 };
158158
159−static struct attribute_group tc1100_attribute_group = {
159+static const struct attribute_group tc1100_attribute_group = {
160160 .attrs = tc1100_attributes,
161161 };
162162
drivers/platform/x86/think-lmi.c+904−0
@@ -0,0 +1,904 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Think LMI BIOS configuration driver
4+ *
5+ * Copyright(C) 2019-2021 Lenovo
6+ *
7+ * Original code from Thinkpad-wmi project https://github.com/iksaif/thinkpad-wmi
8+ * Copyright(C) 2017 Corentin Chary <corentin.chary@gmail.com>
9+ * Distributed under the GPL-2.0 license
10+ */
11+
12+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13+
14+#include <linux/acpi.h>
15+#include <linux/errno.h>
16+#include <linux/fs.h>
17+#include <linux/string.h>
18+#include <linux/types.h>
19+#include <linux/wmi.h>
20+#include "firmware_attributes_class.h"
21+#include "think-lmi.h"
22+
23+/*
24+ * Name:
25+ * Lenovo_BiosSetting
26+ * Description:
27+ * Get item name and settings for current LMI instance.
28+ * Type:
29+ * Query
30+ * Returns:
31+ * "Item,Value"
32+ * Example:
33+ * "WakeOnLAN,Enable"
34+ */
35+#define LENOVO_BIOS_SETTING_GUID "51F5230E-9677-46CD-A1CF-C0B23EE34DB7"
36+
37+/*
38+ * Name:
39+ * Lenovo_SetBiosSetting
40+ * Description:
41+ * Change the BIOS setting to the desired value using the Lenovo_SetBiosSetting
42+ * class. To save the settings, use the Lenovo_SaveBiosSetting class.
43+ * BIOS settings and values are case sensitive.
44+ * After making changes to the BIOS settings, you must reboot the computer
45+ * before the changes will take effect.
46+ * Type:
47+ * Method
48+ * Arguments:
49+ * "Item,Value,Password,Encoding,KbdLang;"
50+ * Example:
51+ * "WakeOnLAN,Disable,pa55w0rd,ascii,us;"
52+ */
53+#define LENOVO_SET_BIOS_SETTINGS_GUID "98479A64-33F5-4E33-A707-8E251EBBC3A1"
54+
55+/*
56+ * Name:
57+ * Lenovo_SaveBiosSettings
58+ * Description:
59+ * Save any pending changes in settings.
60+ * Type:
61+ * Method
62+ * Arguments:
63+ * "Password,Encoding,KbdLang;"
64+ * Example:
65+ * "pa55w0rd,ascii,us;"
66+ */
67+#define LENOVO_SAVE_BIOS_SETTINGS_GUID "6A4B54EF-A5ED-4D33-9455-B0D9B48DF4B3"
68+
69+/*
70+ * Name:
71+ * Lenovo_BiosPasswordSettings
72+ * Description:
73+ * Return BIOS Password settings
74+ * Type:
75+ * Query
76+ * Returns:
77+ * PasswordMode, PasswordState, MinLength, MaxLength,
78+ * SupportedEncoding, SupportedKeyboard
79+ */
80+#define LENOVO_BIOS_PASSWORD_SETTINGS_GUID "8ADB159E-1E32-455C-BC93-308A7ED98246"
81+
82+/*
83+ * Name:
84+ * Lenovo_SetBiosPassword
85+ * Description:
86+ * Change a specific password.
87+ * - BIOS settings cannot be changed at the same boot as power-on
88+ * passwords (POP) and hard disk passwords (HDP). If you want to change
89+ * BIOS settings and POP or HDP, you must reboot the system after changing
90+ * one of them.
91+ * - A password cannot be set using this method when one does not already
92+ * exist. Passwords can only be updated or cleared.
93+ * Type:
94+ * Method
95+ * Arguments:
96+ * "PasswordType,CurrentPassword,NewPassword,Encoding,KbdLang;"
97+ * Example:
98+ * "pop,pa55w0rd,newpa55w0rd,ascii,us;”
99+ */
100+#define LENOVO_SET_BIOS_PASSWORD_GUID "2651D9FD-911C-4B69-B94E-D0DED5963BD7"
101+
102+/*
103+ * Name:
104+ * Lenovo_GetBiosSelections
105+ * Description:
106+ * Return a list of valid settings for a given item.
107+ * Type:
108+ * Method
109+ * Arguments:
110+ * "Item"
111+ * Returns:
112+ * "Value1,Value2,Value3,..."
113+ * Example:
114+ * -> "FlashOverLAN"
115+ * <- "Enabled,Disabled"
116+ */
117+#define LENOVO_GET_BIOS_SELECTIONS_GUID "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B"
118+
119+#define TLMI_POP_PWD (1 << 0)
120+#define TLMI_PAP_PWD (1 << 1)
121+#define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj)
122+#define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj)
123+
124+static const struct tlmi_err_codes tlmi_errs[] = {
125+ {"Success", 0},
126+ {"Not Supported", -EOPNOTSUPP},
127+ {"Invalid Parameter", -EINVAL},
128+ {"Access Denied", -EACCES},
129+ {"System Busy", -EBUSY},
130+};
131+
132+static const char * const encoding_options[] = {
133+ [TLMI_ENCODING_ASCII] = "ascii",
134+ [TLMI_ENCODING_SCANCODE] = "scancode",
135+};
136+static struct think_lmi tlmi_priv;
137+static struct class *fw_attr_class;
138+
139+/* ------ Utility functions ------------*/
140+/* Convert BIOS WMI error string to suitable error code */
141+static int tlmi_errstr_to_err(const char *errstr)
142+{
143+ int i;
144+
145+ for (i = 0; i < sizeof(tlmi_errs)/sizeof(struct tlmi_err_codes); i++) {
146+ if (!strcmp(tlmi_errs[i].err_str, errstr))
147+ return tlmi_errs[i].err_code;
148+ }
149+ return -EPERM;
150+}
151+
152+/* Extract error string from WMI return buffer */
153+static int tlmi_extract_error(const struct acpi_buffer *output)
154+{
155+ const union acpi_object *obj;
156+
157+ obj = output->pointer;
158+ if (!obj)
159+ return -ENOMEM;
160+ if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer)
161+ return -EIO;
162+
163+ return tlmi_errstr_to_err(obj->string.pointer);
164+}
165+
166+/* Utility function to execute WMI call to BIOS */
167+static int tlmi_simple_call(const char *guid, const char *arg)
168+{
169+ const struct acpi_buffer input = { strlen(arg), (char *)arg };
170+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
171+ acpi_status status;
172+ int i, err;
173+
174+ /*
175+ * Duplicated call required to match BIOS workaround for behavior
176+ * seen when WMI accessed via scripting on other OS.
177+ */
178+ for (i = 0; i < 2; i++) {
179+ /* (re)initialize output buffer to default state */
180+ output.length = ACPI_ALLOCATE_BUFFER;
181+ output.pointer = NULL;
182+
183+ status = wmi_evaluate_method(guid, 0, 0, &input, &output);
184+ if (ACPI_FAILURE(status)) {
185+ kfree(output.pointer);
186+ return -EIO;
187+ }
188+ err = tlmi_extract_error(&output);
189+ kfree(output.pointer);
190+ if (err)
191+ return err;
192+ }
193+ return 0;
194+}
195+
196+/* Extract output string from WMI return buffer */
197+static int tlmi_extract_output_string(const struct acpi_buffer *output,
198+ char **string)
199+{
200+ const union acpi_object *obj;
201+ char *s;
202+
203+ obj = output->pointer;
204+ if (!obj)
205+ return -ENOMEM;
206+ if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer)
207+ return -EIO;
208+
209+ s = kstrdup(obj->string.pointer, GFP_KERNEL);
210+ if (!s)
211+ return -ENOMEM;
212+ *string = s;
213+ return 0;
214+}
215+
216+/* ------ Core interface functions ------------*/
217+
218+/* Get password settings from BIOS */
219+static int tlmi_get_pwd_settings(struct tlmi_pwdcfg *pwdcfg)
220+{
221+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
222+ const union acpi_object *obj;
223+ acpi_status status;
224+
225+ if (!tlmi_priv.can_get_password_settings)
226+ return -EOPNOTSUPP;
227+
228+ status = wmi_query_block(LENOVO_BIOS_PASSWORD_SETTINGS_GUID, 0,
229+ &output);
230+ if (ACPI_FAILURE(status))
231+ return -EIO;
232+
233+ obj = output.pointer;
234+ if (!obj)
235+ return -ENOMEM;
236+ if (obj->type != ACPI_TYPE_BUFFER || !obj->buffer.pointer) {
237+ kfree(obj);
238+ return -EIO;
239+ }
240+ /*
241+ * The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad.
242+ * To make the driver compatible on different brands, we permit it to get
243+ * the data in below case.
244+ */
245+ if (obj->buffer.length < sizeof(struct tlmi_pwdcfg)) {
246+ pr_warn("Unknown pwdcfg buffer length %d\n", obj->buffer.length);
247+ kfree(obj);
248+ return -EIO;
249+ }
250+ memcpy(pwdcfg, obj->buffer.pointer, sizeof(struct tlmi_pwdcfg));
251+ kfree(obj);
252+ return 0;
253+}
254+
255+static int tlmi_save_bios_settings(const char *password)
256+{
257+ return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID,
258+ password);
259+}
260+
261+static int tlmi_setting(int item, char **value, const char *guid_string)
262+{
263+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
264+ acpi_status status;
265+ int ret;
266+
267+ status = wmi_query_block(guid_string, item, &output);
268+ if (ACPI_FAILURE(status)) {
269+ kfree(output.pointer);
270+ return -EIO;
271+ }
272+
273+ ret = tlmi_extract_output_string(&output, value);
274+ kfree(output.pointer);
275+ return ret;
276+}
277+
278+static int tlmi_get_bios_selections(const char *item, char **value)
279+{
280+ const struct acpi_buffer input = { strlen(item), (char *)item };
281+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
282+ acpi_status status;
283+ int ret;
284+
285+ status = wmi_evaluate_method(LENOVO_GET_BIOS_SELECTIONS_GUID,
286+ 0, 0, &input, &output);
287+
288+ if (ACPI_FAILURE(status)) {
289+ kfree(output.pointer);
290+ return -EIO;
291+ }
292+
293+ ret = tlmi_extract_output_string(&output, value);
294+ kfree(output.pointer);
295+ return ret;
296+}
297+
298+/* ---- Authentication sysfs --------------------------------------------------------- */
299+static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr,
300+ char *buf)
301+{
302+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
303+
304+ return sysfs_emit(buf, "%d\n", setting->valid);
305+}
306+
307+static struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled);
308+
309+static ssize_t current_password_store(struct kobject *kobj,
310+ struct kobj_attribute *attr,
311+ const char *buf, size_t count)
312+{
313+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
314+ size_t pwdlen;
315+ char *p;
316+
317+ pwdlen = strlen(buf);
318+ /* pwdlen == 0 is allowed to clear the password */
319+ if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen)))
320+ return -EINVAL;
321+
322+ strscpy(setting->password, buf, setting->maxlen);
323+ /* Strip out CR if one is present, setting password won't work if it is present */
324+ p = strchrnul(setting->password, '\n');
325+ *p = '\0';
326+ return count;
327+}
328+
329+static struct kobj_attribute auth_current_password = __ATTR_WO(current_password);
330+
331+static ssize_t new_password_store(struct kobject *kobj,
332+ struct kobj_attribute *attr,
333+ const char *buf, size_t count)
334+{
335+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
336+ char *auth_str, *new_pwd, *p;
337+ size_t pwdlen;
338+ int ret;
339+
340+ if (!capable(CAP_SYS_ADMIN))
341+ return -EPERM;
342+
343+ if (!tlmi_priv.can_set_bios_password)
344+ return -EOPNOTSUPP;
345+
346+ new_pwd = kstrdup(buf, GFP_KERNEL);
347+ if (!new_pwd)
348+ return -ENOMEM;
349+
350+ /* Strip out CR if one is present, setting password won't work if it is present */
351+ p = strchrnul(new_pwd, '\n');
352+ *p = '\0';
353+
354+ pwdlen = strlen(new_pwd);
355+ /* pwdlen == 0 is allowed to clear the password */
356+ if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) {
357+ ret = -EINVAL;
358+ goto out;
359+ }
360+
361+ /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
362+ auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;",
363+ setting->pwd_type, setting->password, new_pwd,
364+ encoding_options[setting->encoding], setting->kbdlang);
365+ if (!auth_str) {
366+ ret = -ENOMEM;
367+ goto out;
368+ }
369+ ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, auth_str);
370+ kfree(auth_str);
371+out:
372+ kfree(new_pwd);
373+ return ret ?: count;
374+}
375+
376+static struct kobj_attribute auth_new_password = __ATTR_WO(new_password);
377+
378+static ssize_t min_password_length_show(struct kobject *kobj, struct kobj_attribute *attr,
379+ char *buf)
380+{
381+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
382+
383+ return sysfs_emit(buf, "%d\n", setting->minlen);
384+}
385+
386+static struct kobj_attribute auth_min_pass_length = __ATTR_RO(min_password_length);
387+
388+static ssize_t max_password_length_show(struct kobject *kobj, struct kobj_attribute *attr,
389+ char *buf)
390+{
391+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
392+
393+ return sysfs_emit(buf, "%d\n", setting->maxlen);
394+}
395+static struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_length);
396+
397+static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr,
398+ char *buf)
399+{
400+ return sysfs_emit(buf, "password\n");
401+}
402+static struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism);
403+
404+static ssize_t encoding_show(struct kobject *kobj, struct kobj_attribute *attr,
405+ char *buf)
406+{
407+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
408+
409+ return sysfs_emit(buf, "%s\n", encoding_options[setting->encoding]);
410+}
411+
412+static ssize_t encoding_store(struct kobject *kobj,
413+ struct kobj_attribute *attr,
414+ const char *buf, size_t count)
415+{
416+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
417+ int i;
418+
419+ /* Scan for a matching profile */
420+ i = sysfs_match_string(encoding_options, buf);
421+ if (i < 0)
422+ return -EINVAL;
423+
424+ setting->encoding = i;
425+ return count;
426+}
427+
428+static struct kobj_attribute auth_encoding = __ATTR_RW(encoding);
429+
430+static ssize_t kbdlang_show(struct kobject *kobj, struct kobj_attribute *attr,
431+ char *buf)
432+{
433+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
434+
435+ return sysfs_emit(buf, "%s\n", setting->kbdlang);
436+}
437+
438+static ssize_t kbdlang_store(struct kobject *kobj,
439+ struct kobj_attribute *attr,
440+ const char *buf, size_t count)
441+{
442+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
443+ int length;
444+
445+ /* Calculate length till '\n' or terminating 0 */
446+ length = strchrnul(buf, '\n') - buf;
447+ if (!length || length >= TLMI_LANG_MAXLEN)
448+ return -EINVAL;
449+
450+ memcpy(setting->kbdlang, buf, length);
451+ setting->kbdlang[length] = '\0';
452+ return count;
453+}
454+
455+static struct kobj_attribute auth_kbdlang = __ATTR_RW(kbdlang);
456+
457+static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr,
458+ char *buf)
459+{
460+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
461+
462+ return sysfs_emit(buf, "%s\n", setting->role);
463+}
464+static struct kobj_attribute auth_role = __ATTR_RO(role);
465+
466+static struct attribute *auth_attrs[] = {
467+ &auth_is_pass_set.attr,
468+ &auth_min_pass_length.attr,
469+ &auth_max_pass_length.attr,
470+ &auth_current_password.attr,
471+ &auth_new_password.attr,
472+ &auth_role.attr,
473+ &auth_mechanism.attr,
474+ &auth_encoding.attr,
475+ &auth_kbdlang.attr,
476+ NULL
477+};
478+
479+static const struct attribute_group auth_attr_group = {
480+ .attrs = auth_attrs,
481+};
482+
483+/* ---- Attributes sysfs --------------------------------------------------------- */
484+static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr,
485+ char *buf)
486+{
487+ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
488+
489+ return sysfs_emit(buf, "%s\n", setting->display_name);
490+}
491+
492+static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
493+{
494+ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
495+ char *item, *value;
496+ int ret;
497+
498+ ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID);
499+ if (ret)
500+ return ret;
501+
502+ /* validate and split from `item,value` -> `value` */
503+ value = strpbrk(item, ",");
504+ if (!value || value == item || !strlen(value + 1))
505+ return -EINVAL;
506+
507+ ret = sysfs_emit(buf, "%s\n", value + 1);
508+ kfree(item);
509+ return ret;
510+}
511+
512+static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
513+{
514+ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
515+
516+ if (!tlmi_priv.can_get_bios_selections)
517+ return -EOPNOTSUPP;
518+
519+ return sysfs_emit(buf, "%s\n", setting->possible_values);
520+}
521+
522+static ssize_t current_value_store(struct kobject *kobj,
523+ struct kobj_attribute *attr,
524+ const char *buf, size_t count)
525+{
526+ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
527+ char *set_str = NULL, *new_setting = NULL;
528+ char *auth_str = NULL;
529+ char *p;
530+ int ret;
531+
532+ if (!tlmi_priv.can_set_bios_settings)
533+ return -EOPNOTSUPP;
534+
535+ new_setting = kstrdup(buf, GFP_KERNEL);
536+ if (!new_setting)
537+ return -ENOMEM;
538+
539+ /* Strip out CR if one is present */
540+ p = strchrnul(new_setting, '\n');
541+ *p = '\0';
542+
543+ if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
544+ auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
545+ tlmi_priv.pwd_admin->password,
546+ encoding_options[tlmi_priv.pwd_admin->encoding],
547+ tlmi_priv.pwd_admin->kbdlang);
548+ if (!auth_str) {
549+ ret = -ENOMEM;
550+ goto out;
551+ }
552+ }
553+
554+ if (auth_str)
555+ set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name,
556+ new_setting, auth_str);
557+ else
558+ set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name,
559+ new_setting);
560+ if (!set_str) {
561+ ret = -ENOMEM;
562+ goto out;
563+ }
564+
565+ ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str);
566+ if (ret)
567+ goto out;
568+
569+ if (auth_str)
570+ ret = tlmi_save_bios_settings(auth_str);
571+ else
572+ ret = tlmi_save_bios_settings("");
573+
574+out:
575+ kfree(auth_str);
576+ kfree(set_str);
577+ kfree(new_setting);
578+ return ret ?: count;
579+}
580+
581+static struct kobj_attribute attr_displ_name = __ATTR_RO(display_name);
582+
583+static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values);
584+
585+static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600);
586+
587+static struct attribute *tlmi_attrs[] = {
588+ &attr_displ_name.attr,
589+ &attr_current_val.attr,
590+ &attr_possible_values.attr,
591+ NULL
592+};
593+
594+static const struct attribute_group tlmi_attr_group = {
595+ .attrs = tlmi_attrs,
596+};
597+
598+static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr,
599+ char *buf)
600+{
601+ struct kobj_attribute *kattr;
602+
603+ kattr = container_of(attr, struct kobj_attribute, attr);
604+ if (kattr->show)
605+ return kattr->show(kobj, kattr, buf);
606+ return -EIO;
607+}
608+
609+static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr,
610+ const char *buf, size_t count)
611+{
612+ struct kobj_attribute *kattr;
613+
614+ kattr = container_of(attr, struct kobj_attribute, attr);
615+ if (kattr->store)
616+ return kattr->store(kobj, kattr, buf, count);
617+ return -EIO;
618+}
619+
620+static const struct sysfs_ops tlmi_kobj_sysfs_ops = {
621+ .show = tlmi_attr_show,
622+ .store = tlmi_attr_store,
623+};
624+
625+static void tlmi_attr_setting_release(struct kobject *kobj)
626+{
627+ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj);
628+
629+ kfree(setting->possible_values);
630+ kfree(setting);
631+}
632+
633+static void tlmi_pwd_setting_release(struct kobject *kobj)
634+{
635+ struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
636+
637+ kfree(setting);
638+}
639+
640+static struct kobj_type tlmi_attr_setting_ktype = {
641+ .release = &tlmi_attr_setting_release,
642+ .sysfs_ops = &tlmi_kobj_sysfs_ops,
643+};
644+
645+static struct kobj_type tlmi_pwd_setting_ktype = {
646+ .release = &tlmi_pwd_setting_release,
647+ .sysfs_ops = &tlmi_kobj_sysfs_ops,
648+};
649+
650+/* ---- Initialisation --------------------------------------------------------- */
651+static void tlmi_release_attr(void)
652+{
653+ int i;
654+
655+ /* Attribute structures */
656+ for (i = 0; i < TLMI_SETTINGS_COUNT; i++) {
657+ if (tlmi_priv.setting[i]) {
658+ sysfs_remove_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group);
659+ kobject_put(&tlmi_priv.setting[i]->kobj);
660+ }
661+ }
662+ kset_unregister(tlmi_priv.attribute_kset);
663+
664+ /* Authentication structures */
665+ sysfs_remove_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group);
666+ kobject_put(&tlmi_priv.pwd_admin->kobj);
667+ sysfs_remove_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group);
668+ kobject_put(&tlmi_priv.pwd_power->kobj);
669+ kset_unregister(tlmi_priv.authentication_kset);
670+}
671+
672+static int tlmi_sysfs_init(void)
673+{
674+ int i, ret;
675+
676+ ret = fw_attributes_class_get(&fw_attr_class);
677+ if (ret)
678+ return ret;
679+
680+ tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
681+ NULL, "%s", "thinklmi");
682+ if (IS_ERR(tlmi_priv.class_dev)) {
683+ ret = PTR_ERR(tlmi_priv.class_dev);
684+ goto fail_class_created;
685+ }
686+
687+ tlmi_priv.attribute_kset = kset_create_and_add("attributes", NULL,
688+ &tlmi_priv.class_dev->kobj);
689+ if (!tlmi_priv.attribute_kset) {
690+ ret = -ENOMEM;
691+ goto fail_device_created;
692+ }
693+
694+ for (i = 0; i < TLMI_SETTINGS_COUNT; i++) {
695+ /* Check if index is a valid setting - skip if it isn't */
696+ if (!tlmi_priv.setting[i])
697+ continue;
698+
699+ /* check for duplicate or reserved values */
700+ if (kset_find_obj(tlmi_priv.attribute_kset, tlmi_priv.setting[i]->display_name) ||
701+ !strcmp(tlmi_priv.setting[i]->display_name, "Reserved")) {
702+ pr_debug("duplicate or reserved attribute name found - %s\n",
703+ tlmi_priv.setting[i]->display_name);
704+ kfree(tlmi_priv.setting[i]->possible_values);
705+ kfree(tlmi_priv.setting[i]);
706+ tlmi_priv.setting[i] = NULL;
707+ continue;
708+ }
709+
710+ /* Build attribute */
711+ tlmi_priv.setting[i]->kobj.kset = tlmi_priv.attribute_kset;
712+ ret = kobject_init_and_add(&tlmi_priv.setting[i]->kobj, &tlmi_attr_setting_ktype,
713+ NULL, "%s", tlmi_priv.setting[i]->display_name);
714+ if (ret)
715+ goto fail_create_attr;
716+
717+ ret = sysfs_create_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group);
718+ if (ret)
719+ goto fail_create_attr;
720+ }
721+
722+ /* Create authentication entries */
723+ tlmi_priv.authentication_kset = kset_create_and_add("authentication", NULL,
724+ &tlmi_priv.class_dev->kobj);
725+ if (!tlmi_priv.authentication_kset) {
726+ ret = -ENOMEM;
727+ goto fail_create_attr;
728+ }
729+ tlmi_priv.pwd_admin->kobj.kset = tlmi_priv.authentication_kset;
730+ ret = kobject_init_and_add(&tlmi_priv.pwd_admin->kobj, &tlmi_pwd_setting_ktype,
731+ NULL, "%s", "Admin");
732+ if (ret)
733+ goto fail_create_attr;
734+
735+ ret = sysfs_create_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group);
736+ if (ret)
737+ goto fail_create_attr;
738+
739+ tlmi_priv.pwd_power->kobj.kset = tlmi_priv.authentication_kset;
740+ ret = kobject_init_and_add(&tlmi_priv.pwd_power->kobj, &tlmi_pwd_setting_ktype,
741+ NULL, "%s", "System");
742+ if (ret)
743+ goto fail_create_attr;
744+
745+ ret = sysfs_create_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group);
746+ if (ret)
747+ goto fail_create_attr;
748+
749+ return ret;
750+
751+fail_create_attr:
752+ tlmi_release_attr();
753+fail_device_created:
754+ device_destroy(fw_attr_class, MKDEV(0, 0));
755+fail_class_created:
756+ fw_attributes_class_put();
757+ return ret;
758+}
759+
760+/* ---- Base Driver -------------------------------------------------------- */
761+static int tlmi_analyze(void)
762+{
763+ struct tlmi_pwdcfg pwdcfg;
764+ acpi_status status;
765+ int i, ret;
766+
767+ if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) &&
768+ wmi_has_guid(LENOVO_SAVE_BIOS_SETTINGS_GUID))
769+ tlmi_priv.can_set_bios_settings = true;
770+
771+ if (wmi_has_guid(LENOVO_GET_BIOS_SELECTIONS_GUID))
772+ tlmi_priv.can_get_bios_selections = true;
773+
774+ if (wmi_has_guid(LENOVO_SET_BIOS_PASSWORD_GUID))
775+ tlmi_priv.can_set_bios_password = true;
776+
777+ if (wmi_has_guid(LENOVO_BIOS_PASSWORD_SETTINGS_GUID))
778+ tlmi_priv.can_get_password_settings = true;
779+
780+ /*
781+ * Try to find the number of valid settings of this machine
782+ * and use it to create sysfs attributes.
783+ */
784+ for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) {
785+ struct tlmi_attr_setting *setting;
786+ char *item = NULL;
787+ char *p;
788+
789+ tlmi_priv.setting[i] = NULL;
790+ status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID);
791+ if (ACPI_FAILURE(status))
792+ break;
793+ if (!item)
794+ break;
795+ if (!*item)
796+ continue;
797+
798+ /* It is not allowed to have '/' for file name. Convert it into '\'. */
799+ strreplace(item, '/', '\\');
800+
801+ /* Remove the value part */
802+ p = strchrnul(item, ',');
803+ *p = '\0';
804+
805+ /* Create a setting entry */
806+ setting = kzalloc(sizeof(*setting), GFP_KERNEL);
807+ if (!setting) {
808+ ret = -ENOMEM;
809+ goto fail_clear_attr;
810+ }
811+ setting->index = i;
812+ strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN);
813+ /* If BIOS selections supported, load those */
814+ if (tlmi_priv.can_get_bios_selections) {
815+ ret = tlmi_get_bios_selections(setting->display_name,
816+ &setting->possible_values);
817+ if (ret || !setting->possible_values)
818+ pr_info("Error retrieving possible values for %d : %s\n",
819+ i, setting->display_name);
820+ }
821+ tlmi_priv.setting[i] = setting;
822+ tlmi_priv.settings_count++;
823+ kfree(item);
824+ }
825+
826+ /* Create password setting structure */
827+ ret = tlmi_get_pwd_settings(&pwdcfg);
828+ if (ret)
829+ goto fail_clear_attr;
830+
831+ tlmi_priv.pwd_admin = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL);
832+ if (!tlmi_priv.pwd_admin) {
833+ ret = -ENOMEM;
834+ goto fail_clear_attr;
835+ }
836+ strscpy(tlmi_priv.pwd_admin->kbdlang, "us", TLMI_LANG_MAXLEN);
837+ tlmi_priv.pwd_admin->encoding = TLMI_ENCODING_ASCII;
838+ tlmi_priv.pwd_admin->pwd_type = "pap";
839+ tlmi_priv.pwd_admin->role = "bios-admin";
840+ tlmi_priv.pwd_admin->minlen = pwdcfg.min_length;
841+ if (WARN_ON(pwdcfg.max_length >= TLMI_PWD_BUFSIZE))
842+ pwdcfg.max_length = TLMI_PWD_BUFSIZE - 1;
843+ tlmi_priv.pwd_admin->maxlen = pwdcfg.max_length;
844+ if (pwdcfg.password_state & TLMI_PAP_PWD)
845+ tlmi_priv.pwd_admin->valid = true;
846+
847+ tlmi_priv.pwd_power = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL);
848+ if (!tlmi_priv.pwd_power) {
849+ ret = -ENOMEM;
850+ goto fail_clear_attr;
851+ }
852+ strscpy(tlmi_priv.pwd_power->kbdlang, "us", TLMI_LANG_MAXLEN);
853+ tlmi_priv.pwd_power->encoding = TLMI_ENCODING_ASCII;
854+ tlmi_priv.pwd_power->pwd_type = "pop";
855+ tlmi_priv.pwd_power->role = "power-on";
856+ tlmi_priv.pwd_power->minlen = pwdcfg.min_length;
857+ tlmi_priv.pwd_power->maxlen = pwdcfg.max_length;
858+
859+ if (pwdcfg.password_state & TLMI_POP_PWD)
860+ tlmi_priv.pwd_power->valid = true;
861+
862+ return 0;
863+
864+fail_clear_attr:
865+ for (i = 0; i < TLMI_SETTINGS_COUNT; ++i)
866+ kfree(tlmi_priv.setting[i]);
867+ return ret;
868+}
869+
870+static void tlmi_remove(struct wmi_device *wdev)
871+{
872+ tlmi_release_attr();
873+ device_destroy(fw_attr_class, MKDEV(0, 0));
874+ fw_attributes_class_put();
875+}
876+
877+static int tlmi_probe(struct wmi_device *wdev, const void *context)
878+{
879+ tlmi_analyze();
880+ return tlmi_sysfs_init();
881+}
882+
883+static const struct wmi_device_id tlmi_id_table[] = {
884+ { .guid_string = LENOVO_BIOS_SETTING_GUID },
885+ { }
886+};
887+MODULE_DEVICE_TABLE(wmi, tlmi_id_table);
888+
889+static struct wmi_driver tlmi_driver = {
890+ .driver = {
891+ .name = "think-lmi",
892+ },
893+ .id_table = tlmi_id_table,
894+ .probe = tlmi_probe,
895+ .remove = tlmi_remove,
896+};
897+
898+MODULE_AUTHOR("Sugumaran L <slacshiminar@lenovo.com>");
899+MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
900+MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
901+MODULE_DESCRIPTION("ThinkLMI Driver");
902+MODULE_LICENSE("GPL");
903+
904+module_wmi_driver(tlmi_driver);
drivers/platform/x86/think-lmi.h+72−0
@@ -0,0 +1,72 @@
1+/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
3+#ifndef _THINK_LMI_H_
4+#define _THINK_LMI_H_
5+
6+#include <linux/types.h>
7+
8+#define TLMI_SETTINGS_COUNT 256
9+#define TLMI_SETTINGS_MAXLEN 512
10+#define TLMI_PWD_BUFSIZE 129
11+#define TLMI_LANG_MAXLEN 4
12+
13+/* Possible error values */
14+struct tlmi_err_codes {
15+ const char *err_str;
16+ int err_code;
17+};
18+
19+enum encoding_option {
20+ TLMI_ENCODING_ASCII,
21+ TLMI_ENCODING_SCANCODE,
22+};
23+
24+/* password configuration details */
25+struct tlmi_pwdcfg {
26+ uint32_t password_mode;
27+ uint32_t password_state;
28+ uint32_t min_length;
29+ uint32_t max_length;
30+ uint32_t supported_encodings;
31+ uint32_t supported_keyboard;
32+};
33+
34+/* password setting details */
35+struct tlmi_pwd_setting {
36+ struct kobject kobj;
37+ bool valid;
38+ char password[TLMI_PWD_BUFSIZE];
39+ const char *pwd_type;
40+ const char *role;
41+ int minlen;
42+ int maxlen;
43+ enum encoding_option encoding;
44+ char kbdlang[TLMI_LANG_MAXLEN];
45+};
46+
47+/* Attribute setting details */
48+struct tlmi_attr_setting {
49+ struct kobject kobj;
50+ int index;
51+ char display_name[TLMI_SETTINGS_MAXLEN];
52+ char *possible_values;
53+};
54+
55+struct think_lmi {
56+ struct wmi_device *wmi_device;
57+
58+ int settings_count;
59+ bool can_set_bios_settings;
60+ bool can_get_bios_selections;
61+ bool can_set_bios_password;
62+ bool can_get_password_settings;
63+
64+ struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT];
65+ struct device *class_dev;
66+ struct kset *attribute_kset;
67+ struct kset *authentication_kset;
68+ struct tlmi_pwd_setting *pwd_admin;
69+ struct tlmi_pwd_setting *pwd_power;
70+};
71+
72+#endif /* !_THINK_LMI_H_ */
drivers/platform/x86/thinkpad_acpi.c+1−1
@@ -7938,7 +7938,7 @@ static int volume_write(char *buf) · 7937 unmodified lines
79387938 continue;
79397939 } else if (sscanf(cmd, "level %u", &l) == 1 &&
79407940 l >= 0 && l <= TP_EC_VOLUME_MAX) {
7941− new_level = l;
7941+ new_level = l;
79427942 continue;
79437943 }
79447944 }
drivers/platform/x86/toshiba_acpi.c+1−0
@@ -2831,6 +2831,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) · 2830 unmodified lines
28312831
28322832 if (!dev->info_supported && !dev->system_event_supported) {
28332833 pr_warn("No hotkey query interface found\n");
2834+ error = -EINVAL;
28342835 goto err_remove_filter;
28352836 }
28362837
drivers/platform/x86/toshiba_haps.c+1−1
@@ -131,7 +131,7 @@ static const struct attribute_group haps_attr_group = { · 130 unmodified lines
131131 */
132132 static void toshiba_haps_notify(struct acpi_device *device, u32 event)
133133 {
134− pr_debug("Received event: 0x%x", event);
134+ pr_debug("Received event: 0x%x\n", event);
135135
136136 acpi_bus_generate_netlink_event(device->pnp.device_class,
137137 dev_name(&device->dev),
drivers/platform/x86/touchscreen_dmi.c+86−1
@@ -299,6 +299,35 @@ static const struct ts_dmi_data estar_beauty_hd_data = { · 298 unmodified lines
299299 .properties = estar_beauty_hd_props,
300300 };
301301
302+/* Generic props + data for upside-down mounted GDIX1001 touchscreens */
303+static const struct property_entry gdix1001_upside_down_props[] = {
304+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
305+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
306+ { }
307+};
308+
309+static const struct ts_dmi_data gdix1001_00_upside_down_data = {
310+ .acpi_name = "GDIX1001:00",
311+ .properties = gdix1001_upside_down_props,
312+};
313+
314+static const struct ts_dmi_data gdix1001_01_upside_down_data = {
315+ .acpi_name = "GDIX1001:01",
316+ .properties = gdix1001_upside_down_props,
317+};
318+
319+static const struct property_entry glavey_tm800a550l_props[] = {
320+ PROPERTY_ENTRY_STRING("firmware-name", "gt912-glavey-tm800a550l.fw"),
321+ PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-glavey-tm800a550l.cfg"),
322+ PROPERTY_ENTRY_U32("goodix,main-clk", 54),
323+ { }
324+};
325+
326+static const struct ts_dmi_data glavey_tm800a550l_data = {
327+ .acpi_name = "GDIX1001:00",
328+ .properties = glavey_tm800a550l_props,
329+};
330+
302331 static const struct property_entry gp_electronic_t701_props[] = {
303332 PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
304333 PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
@@ -942,7 +971,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 637 unmodified lines
942971 },
943972 },
944973 {
945− /* Chuwi Hi10 Prus (CWI597) */
974+ /* Chuwi Hi10 Pro (CWI529) */
946975 .driver_data = (void *)&chuwi_hi10_pro_data,
947976 .matches = {
948977 DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
@@ -1038,6 +1067,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 89 unmodified lines
10381067 DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"),
10391068 },
10401069 },
1070+ { /* Glavey TM800A550L */
1071+ .driver_data = (void *)&glavey_tm800a550l_data,
1072+ .matches = {
1073+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
1074+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
1075+ /* Above strings are too generic, also match on BIOS version */
1076+ DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
1077+ },
1078+ },
10411079 {
10421080 /* GP-electronic T701 */
10431081 .driver_data = (void *)&gp_electronic_t701_data,
@@ -1330,6 +1368,24 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 286 unmodified lines
13301368 DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"),
13311369 },
13321370 },
1371+ {
1372+ /* Teclast X89 (Android version / BIOS) */
1373+ .driver_data = (void *)&gdix1001_00_upside_down_data,
1374+ .matches = {
1375+ DMI_MATCH(DMI_BOARD_VENDOR, "WISKY"),
1376+ DMI_MATCH(DMI_BOARD_NAME, "3G062i"),
1377+ },
1378+ },
1379+ {
1380+ /* Teclast X89 (Windows version / BIOS) */
1381+ .driver_data = (void *)&gdix1001_01_upside_down_data,
1382+ .matches = {
1383+ /* tPAD is too generic, also match on bios date */
1384+ DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
1385+ DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
1386+ DMI_MATCH(DMI_BIOS_DATE, "12/19/2014"),
1387+ },
1388+ },
13331389 {
13341390 /* Teclast X98 Plus II */
13351391 .driver_data = (void *)&teclast_x98plus2_data,
@@ -1338,6 +1394,19 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 2 unmodified lines
13381394 DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"),
13391395 },
13401396 },
1397+ {
1398+ /* Teclast X98 Pro */
1399+ .driver_data = (void *)&gdix1001_00_upside_down_data,
1400+ .matches = {
1401+ /*
1402+ * Only match BIOS date, because the manufacturers
1403+ * BIOS does not report the board name at all
1404+ * (sometimes)...
1405+ */
1406+ DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
1407+ DMI_MATCH(DMI_BIOS_DATE, "10/28/2015"),
1408+ },
1409+ },
13411410 {
13421411 /* Trekstor Primebook C11 */
13431412 .driver_data = (void *)&trekstor_primebook_c11_data,
@@ -1413,6 +1482,22 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 69 unmodified lines
14131482 DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"),
14141483 },
14151484 },
1485+ {
1486+ /* "WinBook TW100" */
1487+ .driver_data = (void *)&gdix1001_00_upside_down_data,
1488+ .matches = {
1489+ DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
1490+ DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
1491+ }
1492+ },
1493+ {
1494+ /* WinBook TW700 */
1495+ .driver_data = (void *)&gdix1001_00_upside_down_data,
1496+ .matches = {
1497+ DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
1498+ DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
1499+ },
1500+ },
14161501 {
14171502 /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
14181503 .driver_data = (void *)&chuwi_vi8_data,
drivers/platform/x86/uv_sysfs.c+2−2
@@ -778,7 +778,7 @@ static struct attribute *base_attrs[] = { · 777 unmodified lines
778778 NULL,
779779 };
780780
781−static struct attribute_group base_attr_group = {
781+static const struct attribute_group base_attr_group = {
782782 .attrs = base_attrs
783783 };
784784
@@ -823,7 +823,7 @@ static struct attribute *hubless_base_attrs[] = { · 38 unmodified lines
823823 NULL,
824824 };
825825
826−static struct attribute_group hubless_base_attr_group = {
826+static const struct attribute_group hubless_base_attr_group = {
827827 .attrs = hubless_base_attrs
828828 };
829829
drivers/platform/x86/wireless-hotkey.c+103−0
@@ -0,0 +1,103 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Airplane mode button for AMD, HP & Xiaomi laptops
4+ *
5+ * Copyright (C) 2014-2017 Alex Hung <alex.hung@canonical.com>
6+ * Copyright (C) 2021 Advanced Micro Devices
7+ */
8+
9+#include <linux/kernel.h>
10+#include <linux/module.h>
11+#include <linux/init.h>
12+#include <linux/input.h>
13+#include <linux/platform_device.h>
14+#include <linux/acpi.h>
15+#include <acpi/acpi_bus.h>
16+
17+MODULE_LICENSE("GPL");
18+MODULE_AUTHOR("Alex Hung");
19+MODULE_ALIAS("acpi*:HPQ6001:*");
20+MODULE_ALIAS("acpi*:WSTADEF:*");
21+MODULE_ALIAS("acpi*:AMDI0051:*");
22+
23+static struct input_dev *wl_input_dev;
24+
25+static const struct acpi_device_id wl_ids[] = {
26+ {"HPQ6001", 0},
27+ {"WSTADEF", 0},
28+ {"AMDI0051", 0},
29+ {"", 0},
30+};
31+
32+static int wireless_input_setup(void)
33+{
34+ int err;
35+
36+ wl_input_dev = input_allocate_device();
37+ if (!wl_input_dev)
38+ return -ENOMEM;
39+
40+ wl_input_dev->name = "Wireless hotkeys";
41+ wl_input_dev->phys = "hpq6001/input0";
42+ wl_input_dev->id.bustype = BUS_HOST;
43+ wl_input_dev->evbit[0] = BIT(EV_KEY);
44+ set_bit(KEY_RFKILL, wl_input_dev->keybit);
45+
46+ err = input_register_device(wl_input_dev);
47+ if (err)
48+ goto err_free_dev;
49+
50+ return 0;
51+
52+err_free_dev:
53+ input_free_device(wl_input_dev);
54+ return err;
55+}
56+
57+static void wireless_input_destroy(void)
58+{
59+ input_unregister_device(wl_input_dev);
60+}
61+
62+static void wl_notify(struct acpi_device *acpi_dev, u32 event)
63+{
64+ if (event != 0x80) {
65+ pr_info("Received unknown event (0x%x)\n", event);
66+ return;
67+ }
68+
69+ input_report_key(wl_input_dev, KEY_RFKILL, 1);
70+ input_sync(wl_input_dev);
71+ input_report_key(wl_input_dev, KEY_RFKILL, 0);
72+ input_sync(wl_input_dev);
73+}
74+
75+static int wl_add(struct acpi_device *device)
76+{
77+ int err;
78+
79+ err = wireless_input_setup();
80+ if (err)
81+ pr_err("Failed to setup hp wireless hotkeys\n");
82+
83+ return err;
84+}
85+
86+static int wl_remove(struct acpi_device *device)
87+{
88+ wireless_input_destroy();
89+ return 0;
90+}
91+
92+static struct acpi_driver wl_driver = {
93+ .name = "wireless-hotkey",
94+ .owner = THIS_MODULE,
95+ .ids = wl_ids,
96+ .ops = {
97+ .add = wl_add,
98+ .remove = wl_remove,
99+ .notify = wl_notify,
100+ },
101+};
102+
103+module_acpi_driver(wl_driver);
include/linux/acpi.h+7−0
@@ -1097,6 +1097,8 @@ void __acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, const c · 1096 unmodified lines
10971097 #if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
10981098 bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
10991099 struct acpi_resource_gpio **agpio);
1100+bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
1101+ struct acpi_resource_gpio **agpio);
11001102 int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index);
11011103 #else
11021104 static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
@@ -1104,6 +1106,11 @@ static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, · 1 unmodified line
11041106 {
11051107 return false;
11061108 }
1109+static inline bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
1110+ struct acpi_resource_gpio **agpio)
1111+{
1112+ return false;
1113+}
11071114 static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev,
11081115 const char *name, int index)
11091116 {
include/linux/devm-helpers.h+25−0
@@ -51,4 +51,29 @@ static inline int devm_delayed_work_autocancel(struct device *dev, · 50 unmodified lines
5151 return devm_add_action(dev, devm_delayed_work_drop, w);
5252 }
5353
54+static inline void devm_work_drop(void *res)
55+{
56+ cancel_work_sync(res);
57+}
58+
59+/**
60+ * devm_work_autocancel - Resource-managed work allocation
61+ * @dev: Device which lifetime work is bound to
62+ * @w: Work to be added (and automatically cancelled)
63+ * @worker: Worker function
64+ *
65+ * Initialize work which is automatically cancelled when driver is detached.
66+ * A few drivers need to queue work which must be cancelled before driver
67+ * is detached to avoid accessing removed resources.
68+ * devm_work_autocancel() can be used to omit the explicit
69+ * cancelleation when driver is detached.
70+ */
71+static inline int devm_work_autocancel(struct device *dev,
72+ struct work_struct *w,
73+ work_func_t worker)
74+{
75+ INIT_WORK(w, worker);
76+ return devm_add_action(dev, devm_work_drop, w);
77+}
78+
5479 #endif
include/linux/gpio/consumer.h+2−0
@@ -692,6 +692,8 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev, · 691 unmodified lines
692692 const struct acpi_gpio_mapping *gpios);
693693 void devm_acpi_dev_remove_driver_gpios(struct device *dev);
694694
695+struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label);
696+
695697 #else /* CONFIG_GPIOLIB && CONFIG_ACPI */
696698
697699 struct acpi_device;
include/linux/surface_aggregator/controller.h+26−1
@@ -6,7 +6,7 @@ · 5 unmodified lines
66 * managing access and communication to and from the SSAM EC, as well as main
77 * communication structures and definitions.
88 *
9− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
9+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
1010 */
1111
1212 #ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H
@@ -795,6 +795,20 @@ enum ssam_event_mask { · 782 unmodified lines
795795 #define SSAM_EVENT_REGISTRY_REG \
796796 SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02)
797797
798+/**
799+ * enum ssam_event_notifier_flags - Flags for event notifiers.
800+ * @SSAM_EVENT_NOTIFIER_OBSERVER:
801+ * The corresponding notifier acts as observer. Registering a notifier
802+ * with this flag set will not attempt to enable any event. Equally,
803+ * unregistering will not attempt to disable any event. Note that a
804+ * notifier with this flag may not even correspond to a certain event at
805+ * all, only to a specific event target category. Event matching will not
806+ * be influenced by this flag.
807+ */
808+enum ssam_event_notifier_flags {
809+ SSAM_EVENT_NOTIFIER_OBSERVER = BIT(0),
810+};
811+
798812 /**
799813 * struct ssam_event_notifier - Notifier block for SSAM events.
800814 * @base: The base notifier block with callback function and priority.
@@ -803,6 +817,7 @@ enum ssam_event_mask { · 2 unmodified lines
803817 * @event.id: ID specifying the event.
804818 * @event.mask: Flags determining how events are matched to the notifier.
805819 * @event.flags: Flags used for enabling the event.
820+ * @flags: Notifier flags (see &enum ssam_event_notifier_flags).
806821 */
807822 struct ssam_event_notifier {
808823 struct ssam_notifier_block base;
@@ -813,6 +828,8 @@ struct ssam_event_notifier { · 4 unmodified lines
813828 enum ssam_event_mask mask;
814829 u8 flags;
815830 } event;
831+
832+ unsigned long flags;
816833 };
817834
818835 int ssam_notifier_register(struct ssam_controller *ctrl,
@@ -821,4 +838,12 @@ int ssam_notifier_register(struct ssam_controller *ctrl, · 2 unmodified lines
821838 int ssam_notifier_unregister(struct ssam_controller *ctrl,
822839 struct ssam_event_notifier *n);
823840
841+int ssam_controller_event_enable(struct ssam_controller *ctrl,
842+ struct ssam_event_registry reg,
843+ struct ssam_event_id id, u8 flags);
844+
845+int ssam_controller_event_disable(struct ssam_controller *ctrl,
846+ struct ssam_event_registry reg,
847+ struct ssam_event_id id, u8 flags);
848+
824849 #endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */
include/linux/surface_aggregator/device.h+1−1
@@ -7,7 +7,7 @@ · 6 unmodified lines
77 * Provides support for non-platform/non-ACPI SSAM clients via dedicated
88 * subsystem.
99 *
10− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
10+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
1111 */
1212
1313 #ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H
include/linux/surface_aggregator/serial_hub.h+1−1
@@ -6,7 +6,7 @@ · 5 unmodified lines
66 * Surface System Aggregator Module (SSAM). Provides the interface for basic
77 * packet- and request-based communication with the SSAM EC via SSH.
88 *
9− * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>
9+ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
1010 */
1111
1212 #ifndef _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H
include/uapi/linux/surface_aggregator/cdev.h+71−2
@@ -6,7 +6,7 @@ · 5 unmodified lines
66 * device. This device provides direct user-space access to the SSAM EC.
77 * Intended for debugging and development.
88 *
9− * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
9+ * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
1010 */
1111
1212 #ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
@@ -73,6 +73,75 @@ struct ssam_cdev_request { · 60 unmodified lines
7373 } response;
7474 } __attribute__((__packed__));
7575
76−#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request)
76+/**
77+ * struct ssam_cdev_notifier_desc - Notifier descriptor.
78+ * @priority: Priority value determining the order in which notifier
79+ * callbacks will be called. A higher value means higher
80+ * priority, i.e. the associated callback will be executed
81+ * earlier than other (lower priority) callbacks.
82+ * @target_category: The event target category for which this notifier should
83+ * receive events.
84+ *
85+ * Specifies the notifier that should be registered or unregistered,
86+ * specifically with which priority and for which target category of events.
87+ */
88+struct ssam_cdev_notifier_desc {
89+ __s32 priority;
90+ __u8 target_category;
91+} __attribute__((__packed__));
92+
93+/**
94+ * struct ssam_cdev_event_desc - Event descriptor.
95+ * @reg: Registry via which the event will be enabled/disabled.
96+ * @reg.target_category: Target category for the event registry requests.
97+ * @reg.target_id: Target ID for the event registry requests.
98+ * @reg.cid_enable: Command ID for the event-enable request.
99+ * @reg.cid_disable: Command ID for the event-disable request.
100+ * @id: ID specifying the event.
101+ * @id.target_category: Target category of the event source.
102+ * @id.instance: Instance ID of the event source.
103+ * @flags: Flags used for enabling the event.
104+ *
105+ * Specifies which event should be enabled/disabled and how to do that.
106+ */
107+struct ssam_cdev_event_desc {
108+ struct {
109+ __u8 target_category;
110+ __u8 target_id;
111+ __u8 cid_enable;
112+ __u8 cid_disable;
113+ } reg;
114+
115+ struct {
116+ __u8 target_category;
117+ __u8 instance;
118+ } id;
119+
120+ __u8 flags;
121+} __attribute__((__packed__));
122+
123+/**
124+ * struct ssam_cdev_event - SSAM event sent by the EC.
125+ * @target_category: Target category of the event source. See &enum ssam_ssh_tc.
126+ * @target_id: Target ID of the event source.
127+ * @command_id: Command ID of the event.
128+ * @instance_id: Instance ID of the event source.
129+ * @length: Length of the event payload in bytes.
130+ * @data: Event payload data.
131+ */
132+struct ssam_cdev_event {
133+ __u8 target_category;
134+ __u8 target_id;
135+ __u8 command_id;
136+ __u8 instance_id;
137+ __u16 length;
138+ __u8 data[];
139+} __attribute__((__packed__));
140+
141+#define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request)
142+#define SSAM_CDEV_NOTIF_REGISTER _IOW(0xA5, 2, struct ssam_cdev_notifier_desc)
143+#define SSAM_CDEV_NOTIF_UNREGISTER _IOW(0xA5, 3, struct ssam_cdev_notifier_desc)
144+#define SSAM_CDEV_EVENT_ENABLE _IOW(0xA5, 4, struct ssam_cdev_event_desc)
145+#define SSAM_CDEV_EVENT_DISABLE _IOW(0xA5, 5, struct ssam_cdev_event_desc)
77146
78147 #endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */
tools/power/x86/intel-speed-select/isst-config.c+17−1
@@ -15,7 +15,7 @@ struct process_cmd_struct { · 14 unmodified lines
1515 int arg;
1616 };
1717
18−static const char *version_str = "v1.9";
18+static const char *version_str = "v1.10";
1919 static const int supported_api_ver = 1;
2020 static struct isst_if_platform_info isst_platform_info;
2121 static char *progname;
@@ -106,6 +106,22 @@ int is_skx_based_platform(void) · 84 unmodified lines
106106 return 0;
107107 }
108108
109+int is_spr_platform(void)
110+{
111+ if (cpu_model == 0x8F)
112+ return 1;
113+
114+ return 0;
115+}
116+
117+int is_icx_platform(void)
118+{
119+ if (cpu_model == 0x6A || cpu_model == 0x6C)
120+ return 1;
121+
122+ return 0;
123+}
124+
109125 static int update_cpu_model(void)
110126 {
111127 unsigned int ebx, ecx, edx;
tools/power/x86/intel-speed-select/isst-core.c+15−0
@@ -201,6 +201,7 @@ void isst_get_uncore_mem_freq(int cpu, int config_index, · 200 unmodified lines
201201 {
202202 unsigned int resp;
203203 int ret;
204+
204205 ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ,
205206 0, config_index, &resp);
206207 if (ret) {
@@ -209,6 +210,20 @@ void isst_get_uncore_mem_freq(int cpu, int config_index, · 2 unmodified lines
209210 }
210211
211212 ctdp_level->mem_freq = resp & GENMASK(7, 0);
213+ if (is_spr_platform()) {
214+ ctdp_level->mem_freq *= 200;
215+ } else if (is_icx_platform()) {
216+ if (ctdp_level->mem_freq < 7) {
217+ ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10;
218+ ctdp_level->mem_freq /= 10;
219+ if (ctdp_level->mem_freq % 10 > 5)
220+ ctdp_level->mem_freq++;
221+ } else {
222+ ctdp_level->mem_freq = 0;
223+ }
224+ } else {
225+ ctdp_level->mem_freq = 0;
226+ }
212227 debug_printf(
213228 "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n",
214229 cpu, config_index, resp, ctdp_level->mem_freq);
tools/power/x86/intel-speed-select/isst-display.c+1−1
@@ -446,7 +446,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, · 445 unmodified lines
446446 if (ctdp_level->mem_freq) {
447447 snprintf(header, sizeof(header), "mem-frequency(MHz)");
448448 snprintf(value, sizeof(value), "%d",
449− ctdp_level->mem_freq * DISP_FREQ_MULTIPLIER);
449+ ctdp_level->mem_freq);
450450 format_and_print(outf, level + 2, header, value);
451451 }
452452
tools/power/x86/intel-speed-select/isst.h+2−0
@@ -257,5 +257,7 @@ extern int get_cpufreq_base_freq(int cpu); · 256 unmodified lines
257257 extern int isst_read_pm_config(int cpu, int *cp_state, int *cp_cap);
258258 extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg);
259259 extern int is_skx_based_platform(void);
260+extern int is_spr_platform(void);
261+extern int is_icx_platform(void);
260262 extern void isst_trl_display_information(int cpu, FILE *outf, unsigned long long trl);
261263 #endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t