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
197 Drivers may emit a CHANGE uevent when a password is set or unset197 Drivers may emit a CHANGE uevent when a password is set or unset
198 userspace may check it again.198 userspace may check it again.
199 199
200 On Dell systems, if Admin password is set, then all BIOS attributes200 On Dell and Lenovo systems, if Admin password is set, then all BIOS attributes
201 require password validation.201 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"
202 218
203What: /sys/class/firmware-attributes/*/attributes/pending_reboot219What: /sys/class/firmware-attributes/*/attributes/pending_reboot
204Date: February 2021220Date: February 2021
Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi+55−0
@@ -0,0 +1,55 @@
1What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type
2Date: Apr 2021
3KernelVersion: 5.13
4Contact: "perry.yuan@dell.com>"
5Description:
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:
11Attributes:
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
34What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state
35Date: Apr 2021
36KernelVersion: 5.13
37Contact: "perry.yuan@dell.com>"
38Description:
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
43Attributes:
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 @@
1.. SPDX-License-Identifier: GPL-2.0+1.. SPDX-License-Identifier: GPL-2.0+
2 2
3.. |u8| replace:: :c:type:`u8 <u8>`
4.. |u16| replace:: :c:type:`u16 <u16>`
5.. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>`3.. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>`
6.. |ssam_cdev_request_flags| replace:: :c:type:`enum ssam_cdev_request_flags <ssam_cdev_request_flags>`4.. |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>`
7 6
8==============================7==============================
9User-Space EC Interface (cdev)8User-Space EC Interface (cdev)
@@ -23,6 +22,40 @@ These IOCTLs and their respective input/output parameter structs are defined in · 13 unmodified lines
23A small python library and scripts for accessing this interface can be found22A small python library and scripts for accessing this interface can be found
24at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam.23at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam.
25 24
25.. contents::
26
27
28Receiving Events
29================
30
31Events can be received by reading from the device-file. The are represented by
32the |ssam_cdev_event| datatype.
33
34Before events are available to be read, however, the desired notifiers must be
35registered via the ``SSAM_CDEV_NOTIF_REGISTER`` IOCTL. Notifiers are, in
36essence, callbacks, called when the EC sends an event. They are, in this
37interface, associated with a specific target category and device-file-instance.
38They forward any event of this category to the buffer of the corresponding
39instance, from which it can then be read.
40
41Notifiers themselves do not enable events on the EC. Thus, it may additionally
42be necessary to enable events via the ``SSAM_CDEV_EVENT_ENABLE`` IOCTL. While
43notifiers work per-client (i.e. per-device-file-instance), events are enabled
44globally, for the EC and all of its clients (regardless of userspace or
45non-userspace). The ``SSAM_CDEV_EVENT_ENABLE`` and ``SSAM_CDEV_EVENT_DISABLE``
46IOCTLs take care of reference counting the events, such that an event is
47enabled as long as there is a client that has requested it.
48
49Note that enabled events are not automatically disabled once the client
50instance is closed. Therefore any client process (or group of processes) should
51balance their event enable calls with the corresponding event disable calls. It
52is, however, perfectly valid to enable and disable events on different client
53instances. For example, it is valid to set up notifiers and read events on
54client instance ``A``, enable those events on instance ``B`` (note that these
55will also be received by A since events are enabled/disabled globally), and
56after no more events are desired, disable the previously enabled events via
57instance ``C``.
58
26 59
27Controller IOCTLs60Controller IOCTLs
28=================61=================
@@ -45,9 +78,33 @@ The following IOCTLs are provided: · 16 unmodified lines
45 - ``REQUEST``78 - ``REQUEST``
46 - Perform synchronous SAM request.79 - Perform synchronous SAM request.
47 80
81 * - ``0xA5``
82 - ``2``
83 - ``W``
84 - ``NOTIF_REGISTER``
85 - Register event notifier.
48 86
49``REQUEST``87 * - ``0xA5``
50-----------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---------------------
51 108
52Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.109Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``.
53 110
@@ -82,6 +139,66 @@ submitted, and completed (i.e. handed back to user-space) successfully from · 28 unmodified lines
82inside the IOCTL, but the request ``status`` member may still be negative in139inside the IOCTL, but the request ``status`` member may still be negative in
83case the actual execution of the request failed after it has been submitted.140case the actual execution of the request failed after it has been submitted.
84 141
85A full definition of the argument struct is provided below:142A full definition of the argument struct is provided below.
143
144``SSAM_CDEV_NOTIF_REGISTER``
145----------------------------
146
147Defined as ``_IOW(0xA5, 2, struct ssam_cdev_notifier_desc)``.
148
149Register a notifier for the event target category specified in the given
150notifier description with the specified priority. Notifiers registration is
151required to receive events, but does not enable events themselves. After a
152notifier for a specific target category has been registered, all events of that
153category will be forwarded to the userspace client and can then be read from
154the 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
157Only one notifier can be registered per target category and client instance. If
158a notifier has already been registered, this IOCTL will fail with ``-EEXIST``.
159
160Notifiers will automatically be removed when the device file instance is
161closed.
162
163``SSAM_CDEV_NOTIF_UNREGISTER``
164------------------------------
165
166Defined as ``_IOW(0xA5, 3, struct ssam_cdev_notifier_desc)``.
167
168Unregisters the notifier associated with the specified target category. The
169priority field will be ignored by this IOCTL. If no notifier has been
170registered for this client instance and the given category, this IOCTL will
171fail with ``-ENOENT``.
172
173``SSAM_CDEV_EVENT_ENABLE``
174--------------------------
175
176Defined as ``_IOW(0xA5, 4, struct ssam_cdev_event_desc)``.
177
178Enable the event associated with the given event descriptor.
179
180Note that this call will not register a notifier itself, it will only enable
181events on the controller. If you want to receive events by reading from the
182device file, you will need to register the corresponding notifier(s) on that
183instance.
184
185Events are not automatically disabled when the device file is closed. This must
186be done manually, via a call to the ``SSAM_CDEV_EVENT_DISABLE`` IOCTL.
187
188``SSAM_CDEV_EVENT_DISABLE``
189---------------------------
190
191Defined as ``_IOW(0xA5, 5, struct ssam_cdev_event_desc)``.
192
193Disable the event associated with the given event descriptor.
194
195Note that this will not unregister any notifiers. Events may still be received
196and forwarded to user-space after this call. The only safe way of stopping
197events from being received is unregistering all previously registered
198notifiers.
199
200
201Structures and Enums
202====================
86 203
87.. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h204.. 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
3250xA3 90-9F linux/dtlk.h3250xA3 90-9F linux/dtlk.h
3260xA4 00-1F uapi/linux/tee.h Generic TEE subsystem3260xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
3270xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>3270xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
3280xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator3280xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
329 <mailto:luzmaximilian@gmail.com>329 <mailto:luzmaximilian@gmail.com>
3300xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver3300xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver
331 <mailto:luzmaximilian@gmail.com>331 <mailto:luzmaximilian@gmail.com>
MAINTAINERS+22−3
@@ -5187,7 +5187,14 @@ DELL WMI NOTIFICATIONS DRIVER · 5186 unmodified lines
5187M: Matthew Garrett <mjg59@srcf.ucam.org>5187M: Matthew Garrett <mjg59@srcf.ucam.org>
5188M: Pali Rohár <pali@kernel.org>5188M: Pali Rohár <pali@kernel.org>
5189S: Maintained5189S: Maintained
5190F: drivers/platform/x86/dell/dell-wmi.c5190F: drivers/platform/x86/dell/dell-wmi-base.c
5191
5192DELL WMI HARDWARE PRIVACY SUPPORT
5193M: Perry Yuan <Perry.Yuan@dell.com>
5194L: Dell.Client.Kernel@dell.com
5195L: platform-driver-x86@vger.kernel.org
5196S: Maintained
5197F: drivers/platform/x86/dell/dell-wmi-privacy.c
5191 5198
5192DELTA ST MEDIA DRIVER5199DELTA ST MEDIA DRIVER
5193M: Hugues Fruchet <hugues.fruchet@foss.st.com>5200M: Hugues Fruchet <hugues.fruchet@foss.st.com>
@@ -9397,6 +9404,11 @@ S: Maintained · 4203 unmodified lines
9397F: arch/x86/include/asm/intel_scu_ipc.h9404F: arch/x86/include/asm/intel_scu_ipc.h
9398F: drivers/platform/x86/intel_scu_*9405F: drivers/platform/x86/intel_scu_*
9399 9406
9407INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
9408M: Daniel Scally <djrscally@gmail.com>
9409S: Maintained
9410F: drivers/platform/x86/intel/int3472/
9411
9400INTEL SPEED SELECT TECHNOLOGY9412INTEL SPEED SELECT TECHNOLOGY
9401M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>9413M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
9402L: platform-driver-x86@vger.kernel.org9414L: platform-driver-x86@vger.kernel.org
@@ -9417,7 +9429,7 @@ F: include/linux/firmware/intel/stratix10-smc.h · 14 unmodified lines
9417F: include/linux/firmware/intel/stratix10-svc-client.h9429F: include/linux/firmware/intel/stratix10-svc-client.h
9418 9430
9419INTEL TELEMETRY DRIVER9431INTEL TELEMETRY DRIVER
9420M: Rajneesh Bhardwaj <rajneesh.bhardwaj@linux.intel.com>9432M: Rajneesh Bhardwaj <irenic.rajneesh@gmail.com>
9421M: "David E. Box" <david.e.box@linux.intel.com>9433M: "David E. Box" <david.e.box@linux.intel.com>
9422L: platform-driver-x86@vger.kernel.org9434L: platform-driver-x86@vger.kernel.org
9423S: Maintained9435S: Maintained
@@ -12209,7 +12221,7 @@ M: Maximilian Luz <luzmaximilian@gmail.com> · 2785 unmodified lines
12209L: platform-driver-x86@vger.kernel.org12221L: platform-driver-x86@vger.kernel.org
12210S: Maintained12222S: Maintained
12211W: https://github.com/linux-surface/surface-aggregator-module12223W: https://github.com/linux-surface/surface-aggregator-module
12212C: irc://chat.freenode.net/##linux-surface12224C: irc://irc.libera.chat/linux-surface
12213F: Documentation/driver-api/surface_aggregator/12225F: Documentation/driver-api/surface_aggregator/
12214F: drivers/platform/surface/aggregator/12226F: drivers/platform/surface/aggregator/
12215F: drivers/platform/surface/surface_acpi_notify.c12227F: drivers/platform/surface/surface_acpi_notify.c
@@ -18215,6 +18227,13 @@ W: http://thinkwiki.org/wiki/Ibm-acpi · 5999 unmodified lines
18215T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git18227T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
18216F: drivers/platform/x86/thinkpad_acpi.c18228F: drivers/platform/x86/thinkpad_acpi.c
18217 18229
18230THINKPAD LMI DRIVER
18231M: Mark Pearson <markpearson@lenovo.com>
18232L: platform-driver-x86@vger.kernel.org
18233S: Maintained
18234F: Documentation/ABI/testing/sysfs-class-firmware-attributes
18235F: drivers/platform/x86/think-lmi.?
18236
18218THUNDERBOLT DMA TRAFFIC TEST DRIVER18237THUNDERBOLT DMA TRAFFIC TEST DRIVER
18219M: Isaac Hazan <isaac.hazan@intel.com>18238M: Isaac Hazan <isaac.hazan@intel.com>
18220L: linux-usb@vger.kernel.org18239L: linux-usb@vger.kernel.org
drivers/acpi/pmic/Kconfig+1−1
@@ -52,7 +52,7 @@ endif # PMIC_OPREGION · 51 unmodified lines
52 52
53config TPS68470_PMIC_OPREGION53config TPS68470_PMIC_OPREGION
54 bool "ACPI operation region support for TPS68470 PMIC"54 bool "ACPI operation region support for TPS68470 PMIC"
55 depends on MFD_TPS6847055 depends on INTEL_SKL_INT3472
56 help56 help
57 This config adds ACPI operation region support for TI TPS68470 PMIC.57 This config adds ACPI operation region support for TI TPS68470 PMIC.
58 TPS68470 device is an advanced power management unit that powers58 TPS68470 device is an advanced power management unit that powers
drivers/extcon/extcon-max14577.c+5−11
@@ -6,6 +6,7 @@ · 5 unmodified lines
6// Chanwoo Choi <cw00.choi@samsung.com>6// Chanwoo Choi <cw00.choi@samsung.com>
7// Krzysztof Kozlowski <krzk@kernel.org>7// Krzysztof Kozlowski <krzk@kernel.org>
8 8
9#include <linux/devm-helpers.h>
9#include <linux/kernel.h>10#include <linux/kernel.h>
10#include <linux/module.h>11#include <linux/module.h>
11#include <linux/i2c.h>12#include <linux/i2c.h>
@@ -673,7 +674,10 @@ static int max14577_muic_probe(struct platform_device *pdev) · 661 unmodified lines
673 platform_set_drvdata(pdev, info);674 platform_set_drvdata(pdev, info);
674 mutex_init(&info->mutex);675 mutex_init(&info->mutex);
675 676
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;
677 681
678 switch (max14577->dev_type) {682 switch (max14577->dev_type) {
679 case MAXIM_DEVICE_TYPE_MAX77836:683 case MAXIM_DEVICE_TYPE_MAX77836:
@@ -766,15 +770,6 @@ static int max14577_muic_probe(struct platform_device *pdev) · 86 unmodified lines
766 return ret;770 return ret;
767}771}
768 772
769static 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
778static const struct platform_device_id max14577_muic_id[] = {773static const struct platform_device_id max14577_muic_id[] = {
779 { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },774 { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
780 { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },775 { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
@@ -797,7 +792,6 @@ static struct platform_driver max14577_muic_driver = { · 16 unmodified lines
797 .of_match_table = of_max14577_muic_dt_match,792 .of_match_table = of_max14577_muic_dt_match,
798 },793 },
799 .probe = max14577_muic_probe,794 .probe = max14577_muic_probe,
800 .remove = max14577_muic_remove,
801 .id_table = max14577_muic_id,795 .id_table = max14577_muic_id,
802};796};
803 797
drivers/extcon/extcon-max77693.c+5−12
@@ -5,6 +5,7 @@ · 4 unmodified lines
5// Copyright (C) 2012 Samsung Electrnoics5// Copyright (C) 2012 Samsung Electrnoics
6// Chanwoo Choi <cw00.choi@samsung.com>6// Chanwoo Choi <cw00.choi@samsung.com>
7 7
8#include <linux/devm-helpers.h>
8#include <linux/kernel.h>9#include <linux/kernel.h>
9#include <linux/module.h>10#include <linux/module.h>
10#include <linux/i2c.h>11#include <linux/i2c.h>
@@ -1127,7 +1128,10 @@ static int max77693_muic_probe(struct platform_device *pdev) · 1116 unmodified lines
1127 platform_set_drvdata(pdev, info);1128 platform_set_drvdata(pdev, info);
1128 mutex_init(&info->mutex);1129 mutex_init(&info->mutex);
1129 1130
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;
1131 1135
1132 /* Support irq domain for MAX77693 MUIC device */1136 /* Support irq domain for MAX77693 MUIC device */
1133 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {1137 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
1254 return ret;1258 return ret;
1255}1259}
1256 1260
1257static 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
1267static struct platform_driver max77693_muic_driver = {1261static struct platform_driver max77693_muic_driver = {
1268 .driver = {1262 .driver = {
1269 .name = DEV_NAME,1263 .name = DEV_NAME,
1270 },1264 },
1271 .probe = max77693_muic_probe,1265 .probe = max77693_muic_probe,
1272 .remove = max77693_muic_remove,
1273};1266};
1274 1267
1275module_platform_driver(max77693_muic_driver);1268module_platform_driver(max77693_muic_driver);
drivers/extcon/extcon-max8997.c+15−30
@@ -5,6 +5,7 @@ · 4 unmodified lines
5// Copyright (C) 2012 Samsung Electronics5// Copyright (C) 2012 Samsung Electronics
6// Donggeun Kim <dg77.kim@samsung.com>6// Donggeun Kim <dg77.kim@samsung.com>
7 7
8#include <linux/devm-helpers.h>
8#include <linux/kernel.h>9#include <linux/kernel.h>
9#include <linux/module.h>10#include <linux/module.h>
10#include <linux/i2c.h>11#include <linux/i2c.h>
@@ -650,27 +651,30 @@ static int max8997_muic_probe(struct platform_device *pdev) · 639 unmodified lines
650 mutex_init(&info->mutex);651 mutex_init(&info->mutex);
651 652
652 INIT_WORK(&info->irq_work, max8997_muic_irq_work);653 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;
653 658
654 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {659 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
655 struct max8997_muic_irq *muic_irq = &muic_irqs[i];660 struct max8997_muic_irq *muic_irq = &muic_irqs[i];
656 unsigned int virq = 0;661 unsigned int virq = 0;
657 662
658 virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);663 virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
659 if (!virq) {664 if (!virq)
660 ret = -EINVAL;665 return -EINVAL;
661 goto err_irq;666
662 }
663 muic_irq->virq = virq;667 muic_irq->virq = virq;
664 668
665 ret = request_threaded_irq(virq, NULL,669 ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
666 max8997_muic_irq_handler,670 max8997_muic_irq_handler,
667 IRQF_NO_SUSPEND,671 IRQF_NO_SUSPEND,
668 muic_irq->name, info);672 muic_irq->name, info);
669 if (ret) {673 if (ret) {
670 dev_err(&pdev->dev,674 dev_err(&pdev->dev,
671 "failed: irq request (IRQ: %d, error :%d)\n",675 "failed: irq request (IRQ: %d, error :%d)\n",
672 muic_irq->irq, ret);676 muic_irq->irq, ret);
673 goto err_irq;677 return ret;
674 }678 }
675 }679 }
676 680
@@ -678,14 +682,13 @@ static int max8997_muic_probe(struct platform_device *pdev) · 1 unmodified line
678 info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);682 info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
679 if (IS_ERR(info->edev)) {683 if (IS_ERR(info->edev)) {
680 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");684 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
681 ret = PTR_ERR(info->edev);685 return PTR_ERR(info->edev);
682 goto err_irq;
683 }686 }
684 687
685 ret = devm_extcon_dev_register(&pdev->dev, info->edev);688 ret = devm_extcon_dev_register(&pdev->dev, info->edev);
686 if (ret) {689 if (ret) {
687 dev_err(&pdev->dev, "failed to register extcon device\n");690 dev_err(&pdev->dev, "failed to register extcon device\n");
688 goto err_irq;691 return ret;
689 }692 }
690 693
691 if (pdata && pdata->muic_pdata) {694 if (pdata && pdata->muic_pdata) {
@@ -756,23 +759,6 @@ static int max8997_muic_probe(struct platform_device *pdev) · 64 unmodified lines
756 delay_jiffies);759 delay_jiffies);
757 760
758 return 0;761 return 0;
759
760err_irq:
761 while (--i >= 0)
762 free_irq(muic_irqs[i].virq, info);
763 return ret;
764}
765
766static 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;
776}762}
777 763
778static struct platform_driver max8997_muic_driver = {764static struct platform_driver max8997_muic_driver = {
@@ -780,7 +766,6 @@ static struct platform_driver max8997_muic_driver = { · 1 unmodified line
780 .name = DEV_NAME,766 .name = DEV_NAME,
781 },767 },
782 .probe = max8997_muic_probe,768 .probe = max8997_muic_probe,
783 .remove = max8997_muic_remove,
784};769};
785 770
786module_platform_driver(max8997_muic_driver);771module_platform_driver(max8997_muic_driver);
drivers/gpio/Kconfig+1−1
@@ -1367,7 +1367,7 @@ config GPIO_TPS65912 · 1366 unmodified lines
1367 1367
1368config GPIO_TPS684701368config GPIO_TPS68470
1369 bool "TPS68470 GPIO"1369 bool "TPS68470 GPIO"
1370 depends on MFD_TPS684701370 depends on INTEL_SKL_INT3472
1371 help1371 help
1372 Select this option to enable GPIO driver for the TPS684701372 Select this option to enable GPIO driver for the TPS68470
1373 chip family.1373 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
339 if (!cg)339 if (!cg)
340 return -ENOMEM;340 return -ENOMEM;
341 341
342 platform_set_drvdata(pdev, cg);
343
344 mutex_init(&cg->buslock);342 mutex_init(&cg->buslock);
345 cg->chip.label = KBUILD_MODNAME;343 cg->chip.label = KBUILD_MODNAME;
346 cg->chip.direction_input = crystalcove_gpio_dir_in;344 cg->chip.direction_input = crystalcove_gpio_dir_in;
@@ -372,13 +370,7 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) · 25 unmodified lines
372 return retval;370 return retval;
373 }371 }
374 372
375 retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg);373 return 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;
382}374}
383 375
384static struct platform_driver crystalcove_gpio_driver = {376static struct platform_driver crystalcove_gpio_driver = {
drivers/gpio/gpio-wcove.c+19−20
@@ -99,19 +99,14 @@ struct wcove_gpio { · 98 unmodified lines
99 bool set_irq_mask;99 bool set_irq_mask;
100};100};
101 101
102static inline int to_reg(int gpio, enum ctrl_register reg_type)102static inline int to_reg(int gpio, enum ctrl_register type)
103{103{
104 unsigned int reg;104 unsigned int reg = type == CTRL_IN ? GPIO_IN_CTRL_BASE : GPIO_OUT_CTRL_BASE;
105 105
106 if (gpio >= WCOVE_GPIO_NUM)106 if (gpio >= WCOVE_GPIO_NUM)
107 return -EOPNOTSUPP;107 return -EOPNOTSUPP;
108 108
109 if (reg_type == CTRL_IN)109 return reg + gpio;
110 reg = GPIO_IN_CTRL_BASE + gpio;
111 else
112 reg = GPIO_OUT_CTRL_BASE + gpio;
113
114 return reg;
115}110}
116 111
117static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask)112static 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
129 return reg;124 return reg;
130}125}
131 126
132static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio)127static void wcove_update_irq_mask(struct wcove_gpio *wg, irq_hw_number_t gpio)
133{128{
134 unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask);129 unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask);
135 130
@@ -139,13 +134,10 @@ static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) · 3 unmodified lines
139 regmap_clear_bits(wg->regmap, reg, mask);134 regmap_clear_bits(wg->regmap, reg, mask);
140}135}
141 136
142static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)137static void wcove_update_irq_ctrl(struct wcove_gpio *wg, irq_hw_number_t gpio)
143{138{
144 int reg = to_reg(gpio, CTRL_IN);139 int reg = to_reg(gpio, CTRL_IN);
145 140
146 if (reg < 0)
147 return;
148
149 regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt);141 regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt);
150}142}
151 143
@@ -248,8 +240,9 @@ static int wcove_irq_type(struct irq_data *data, unsigned int type) · 96 unmodified lines
248{240{
249 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);241 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
250 struct wcove_gpio *wg = gpiochip_get_data(chip);242 struct wcove_gpio *wg = gpiochip_get_data(chip);
243 irq_hw_number_t gpio = irqd_to_hwirq(data);
251 244
252 if (data->hwirq >= WCOVE_GPIO_NUM)245 if (gpio >= WCOVE_GPIO_NUM)
253 return 0;246 return 0;
254 247
255 switch (type) {248 switch (type) {
@@ -286,7 +279,7 @@ static void wcove_bus_sync_unlock(struct irq_data *data) · 30 unmodified lines
286{279{
287 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);280 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
288 struct wcove_gpio *wg = gpiochip_get_data(chip);281 struct wcove_gpio *wg = gpiochip_get_data(chip);
289 int gpio = data->hwirq;282 irq_hw_number_t gpio = irqd_to_hwirq(data);
290 283
291 if (wg->update & UPDATE_IRQ_TYPE)284 if (wg->update & UPDATE_IRQ_TYPE)
292 wcove_update_irq_ctrl(wg, gpio);285 wcove_update_irq_ctrl(wg, gpio);
@@ -301,8 +294,9 @@ static void wcove_irq_unmask(struct irq_data *data) · 8 unmodified lines
301{294{
302 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);295 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
303 struct wcove_gpio *wg = gpiochip_get_data(chip);296 struct wcove_gpio *wg = gpiochip_get_data(chip);
297 irq_hw_number_t gpio = irqd_to_hwirq(data);
304 298
305 if (data->hwirq >= WCOVE_GPIO_NUM)299 if (gpio >= WCOVE_GPIO_NUM)
306 return;300 return;
307 301
308 wg->set_irq_mask = false;302 wg->set_irq_mask = false;
@@ -313,8 +307,9 @@ static void wcove_irq_mask(struct irq_data *data) · 4 unmodified lines
313{307{
314 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);308 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
315 struct wcove_gpio *wg = gpiochip_get_data(chip);309 struct wcove_gpio *wg = gpiochip_get_data(chip);
310 irq_hw_number_t gpio = irqd_to_hwirq(data);
316 311
317 if (data->hwirq >= WCOVE_GPIO_NUM)312 if (gpio >= WCOVE_GPIO_NUM)
318 return;313 return;
319 314
320 wg->set_irq_mask = true;315 wg->set_irq_mask = true;
@@ -369,8 +364,7 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) · 48 unmodified lines
369 return IRQ_HANDLED;364 return IRQ_HANDLED;
370}365}
371 366
372static void wcove_gpio_dbg_show(struct seq_file *s,367static void wcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
373 struct gpio_chip *chip)
374{368{
375 unsigned int ctlo, ctli, irq_mask, irq_status;369 unsigned int ctlo, ctli, irq_mask, irq_status;
376 struct wcove_gpio *wg = gpiochip_get_data(chip);370 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
379 for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) {373 for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) {
380 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);374 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
381 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli);375 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
382 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask);381 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask);
383 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status);382 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status);
384 if (ret) {383 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");
386 break;385 break;
387 }386 }
388 387
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
128 return gpiochip_get_desc(chip, pin);128 return gpiochip_get_desc(chip, pin);
129}129}
130 130
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 */
142struct 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}
157EXPORT_SYMBOL_GPL(acpi_get_and_request_gpiod);
158
131static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)159static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
132{160{
133 struct acpi_gpio_event *event = data;161 struct acpi_gpio_event *event = data;
@@ -168,6 +196,29 @@ bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, · 34 unmodified lines
168}196}
169EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);197EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);
170 198
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 */
205bool 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}
220EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource);
221
171static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,222static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
172 struct acpi_gpio_event *event)223 struct acpi_gpio_event *event)
173{224{
drivers/input/touchscreen/goodix.c+0−52
@@ -178,51 +178,6 @@ static const unsigned long goodix_irq_flags[] = { · 177 unmodified lines
178 IRQ_TYPE_LEVEL_HIGH,178 IRQ_TYPE_LEVEL_HIGH,
179};179};
180 180
181/*
182 * Those tablets have their coordinates origin at the bottom right
183 * of the tablet, as if rotated 180 degrees
184 */
185static 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
226static const struct dmi_system_id nine_bytes_report[] = {181static const struct dmi_system_id nine_bytes_report[] = {
227#if defined(CONFIG_DMI) && defined(CONFIG_X86)182#if defined(CONFIG_DMI) && defined(CONFIG_X86)
228 {183 {
@@ -1123,13 +1078,6 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) · 894 unmodified lines
1123 ABS_MT_POSITION_Y, ts->prop.max_y);1078 ABS_MT_POSITION_Y, ts->prop.max_y);
1124 }1079 }
1125 1080
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
1133 if (dmi_check_system(nine_bytes_report)) {1081 if (dmi_check_system(nine_bytes_report)) {
1134 ts->contact_size = 9;1082 ts->contact_size = 9;
1135 1083
drivers/mfd/Kconfig+0−18
@@ -1499,24 +1499,6 @@ config MFD_TPS65217 · 1498 unmodified lines
1499 This driver can also be built as a module. If so, the module1499 This driver can also be built as a module. If so, the module
1500 will be called tps65217.1500 will be called tps65217.
1501 1501
1502config 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
1520config MFD_TI_LP873X1502config MFD_TI_LP873X
1521 tristate "TI LP873X Power Management IC"1503 tristate "TI LP873X Power Management IC"
1522 depends on I2C1504 depends on I2C
drivers/mfd/Makefile+0−1
@@ -105,7 +105,6 @@ obj-$(CONFIG_MFD_TPS65910) += tps65910.o · 104 unmodified lines
105obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o105obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o
106obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o106obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
107obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o107obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
108obj-$(CONFIG_MFD_TPS68470) += tps68470.o
109obj-$(CONFIG_MFD_TPS80031) += tps80031.o108obj-$(CONFIG_MFD_TPS80031) += tps80031.o
110obj-$(CONFIG_MENELAUS) += menelaus.o109obj-$(CONFIG_MENELAUS) += menelaus.o
111 110
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
22static const struct mfd_cell tps68470s[] = {
23 { .name = "tps68470-gpio" },
24 { .name = "tps68470_pmic_opregion" },
25};
26
27static const struct regmap_config tps68470_regmap_config = {
28 .reg_bits = 8,
29 .val_bits = 8,
30 .max_register = TPS68470_REG_MAX,
31};
32
33static 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
54static 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
85static const struct acpi_device_id tps68470_acpi_ids[] = {
86 {"INT3472"},
87 {},
88};
89
90static struct i2c_driver tps68470_driver = {
91 .driver = {
92 .name = "tps68470",
93 .acpi_match_table = tps68470_acpi_ids,
94 },
95 .probe_new = tps68470_probe,
96};
97builtin_i2c_driver(tps68470_driver);
drivers/platform/surface/aggregator/Kconfig+1−1
@@ -1,5 +1,5 @@
1# SPDX-License-Identifier: GPL-2.0+1# 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>
3 3
4menuconfig SURFACE_AGGREGATOR4menuconfig SURFACE_AGGREGATOR
5 tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"5 tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
drivers/platform/surface/aggregator/Makefile+1−1
@@ -1,5 +1,5 @@
1# SPDX-License-Identifier: GPL-2.0+1# 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>
3 3
4# For include/trace/define_trace.h to include trace.h4# For include/trace/define_trace.h to include trace.h
5CFLAGS_core.o = -I$(src)5CFLAGS_core.o = -I$(src)
drivers/platform/surface/aggregator/bus.c+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * Surface System Aggregator Module bus and device integration.3 * Surface System Aggregator Module bus and device integration.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#include <linux/device.h>8#include <linux/device.h>
drivers/platform/surface/aggregator/bus.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * Surface System Aggregator Module bus and device integration.3 * Surface System Aggregator Module bus and device integration.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#ifndef _SURFACE_AGGREGATOR_BUS_H8#ifndef _SURFACE_AGGREGATOR_BUS_H
drivers/platform/surface/aggregator/controller.c+271−61
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * Main SSAM/SSH controller structure and functionality.3 * Main SSAM/SSH controller structure and functionality.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#include <linux/acpi.h>8#include <linux/acpi.h>
@@ -407,6 +407,31 @@ ssam_nf_refcount_dec(struct ssam_nf *nf, struct ssam_event_registry reg, · 398 unmodified lines
407 return NULL;407 return NULL;
408}408}
409 409
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 */
422static 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
410/**435/**
411 * ssam_nf_refcount_empty() - Test if the notification system has any436 * ssam_nf_refcount_empty() - Test if the notification system has any
412 * enabled/active events.437 * enabled/active events.
@@ -2122,14 +2147,123 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl) · 1709 unmodified lines
2122 2147
2123/* -- Top-level event registry interface. ----------------------------------- */2148/* -- Top-level event registry interface. ----------------------------------- */
2124 2149
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 */
2171static 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 */
2225static 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
2125/**2253/**
2126 * ssam_notifier_register() - Register an event notifier.2254 * ssam_notifier_register() - Register an event notifier.
2127 * @ctrl: The controller to register the notifier on.2255 * @ctrl: The controller to register the notifier on.
2128 * @n: The event notifier to register.2256 * @n: The event notifier to register.
2129 *2257 *
2130 * Register an event notifier and increment the usage counter of the2258 * Register an event notifier. Increment the usage counter of the associated
2131 * associated SAM event. If the event was previously not enabled, it will be2259 * SAM event if the notifier is not marked as an observer. If the event is not
2132 * enabled during this call.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.
2133 *2267 *
2134 * Return: Returns zero on success, %-ENOSPC if there have already been2268 * Return: Returns zero on success, %-ENOSPC if there have already been
2135 * %INT_MAX notifiers for the event ID/type associated with the notifier block2269 * %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
2138 * for the specific associated event, returns the status of the event-enable2272 * for the specific associated event, returns the status of the event-enable
2139 * EC-command.2273 * EC-command.
2140 */2274 */
2141int ssam_notifier_register(struct ssam_controller *ctrl,2275int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
2142 struct ssam_event_notifier *n)
2143{2276{
2144 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);2277 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;
2146 struct ssam_nf_head *nf_head;2279 struct ssam_nf_head *nf_head;
2147 struct ssam_nf *nf;2280 struct ssam_nf *nf;
2148 int status;2281 int status;
@@ -2155,44 +2288,32 @@ int ssam_notifier_register(struct ssam_controller *ctrl, · 6 unmodified lines
2155 2288
2156 mutex_lock(&nf->lock);2289 mutex_lock(&nf->lock);
2157 2290
2158 entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);2291 if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
2159 if (IS_ERR(entry)) {2292 entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id);
2160 mutex_unlock(&nf->lock);2293 if (IS_ERR(entry)) {
2161 return PTR_ERR(entry);2294 mutex_unlock(&nf->lock);
2295 return PTR_ERR(entry);
2296 }
2162 }2297 }
2163 2298
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
2168 status = ssam_nfblk_insert(nf_head, &n->base);2299 status = ssam_nfblk_insert(nf_head, &n->base);
2169 if (status) {2300 if (status) {
2170 entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);2301 if (entry)
2171 if (entry->refcount == 0)2302 ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id);
2172 kfree(entry);
2173 2303
2174 mutex_unlock(&nf->lock);2304 mutex_unlock(&nf->lock);
2175 return status;2305 return status;
2176 }2306 }
2177 2307
2178 if (entry->refcount == 1) {2308 if (entry) {
2179 status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id,2309 status = ssam_nf_refcount_enable(ctrl, entry, n->event.flags);
2180 n->event.flags);
2181 if (status) {2310 if (status) {
2182 ssam_nfblk_remove(&n->base);2311 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);
2184 mutex_unlock(&nf->lock);2313 mutex_unlock(&nf->lock);
2185 synchronize_srcu(&nf_head->srcu);2314 synchronize_srcu(&nf_head->srcu);
2186 return status;2315 return status;
2187 }2316 }
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);
2196 }2317 }
2197 2318
2198 mutex_unlock(&nf->lock);2319 mutex_unlock(&nf->lock);
@@ -2205,17 +2326,16 @@ EXPORT_SYMBOL_GPL(ssam_notifier_register); · 6 unmodified lines
2205 * @ctrl: The controller the notifier has been registered on.2326 * @ctrl: The controller the notifier has been registered on.
2206 * @n: The event notifier to unregister.2327 * @n: The event notifier to unregister.
2207 *2328 *
2208 * Unregister an event notifier and decrement the usage counter of the2329 * Unregister an event notifier. Decrement the usage counter of the associated
2209 * associated SAM event. If the usage counter reaches zero, the event will be2330 * SAM event if the notifier is not marked as an observer. If the usage counter
2210 * disabled.2331 * reaches zero, the event will be disabled.
2211 *2332 *
2212 * Return: Returns zero on success, %-ENOENT if the given notifier block has2333 * Return: Returns zero on success, %-ENOENT if the given notifier block has
2213 * not been registered on the controller. If the given notifier block was the2334 * not been registered on the controller. If the given notifier block was the
2214 * last one associated with its specific event, returns the status of the2335 * last one associated with its specific event, returns the status of the
2215 * event-disable EC-command.2336 * event-disable EC-command.
2216 */2337 */
2217int ssam_notifier_unregister(struct ssam_controller *ctrl,2338int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n)
2218 struct ssam_event_notifier *n)
2219{2339{
2220 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);2340 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category);
2221 struct ssam_nf_refcount_entry *entry;2341 struct ssam_nf_refcount_entry *entry;
@@ -2236,33 +2356,24 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, · 14 unmodified lines
2236 return -ENOENT;2356 return -ENOENT;
2237 }2357 }
2238 2358
2239 entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);2359 /*
2240 if (WARN_ON(!entry)) {2360 * If this is an observer notifier, do not attempt to disable the
2241 /*2361 * event, just remove it.
2242 * If this does not return an entry, there's a logic error2362 */
2243 * somewhere: The notifier block is registered, but the event2363 if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) {
2244 * refcount entry is not there. Remove the notifier block2364 entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id);
2245 * anyways.2365 if (WARN_ON(!entry)) {
2246 */2366 /*
2247 status = -ENOENT;2367 * If this does not return an entry, there's a logic
2248 goto remove;2368 * error somewhere: The notifier block is registered,
2249 }2369 * but the event refcount entry is not there. Remove
2250 2370 * the notifier block anyways.
2251 ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n",2371 */
2252 n->event.reg.target_category, n->event.id.target_category,2372 status = -ENOENT;
2253 n->event.id.instance, entry->refcount);2373 goto remove;
2254 2374 }
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 }
2261 2375
2262 if (entry->refcount == 0) {2376 status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags);
2263 status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id,
2264 n->event.flags);
2265 kfree(entry);
2266 }2377 }
2267 2378
2268remove:2379remove:
@@ -2274,6 +2385,105 @@ remove: · 5 unmodified lines
2274}2385}
2275EXPORT_SYMBOL_GPL(ssam_notifier_unregister);2386EXPORT_SYMBOL_GPL(ssam_notifier_unregister);
2276 2387
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 */
2409int 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}
2439EXPORT_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 */
2460int 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}
2485EXPORT_SYMBOL_GPL(ssam_controller_event_disable);
2486
2277/**2487/**
2278 * ssam_notifier_disable_registered() - Disable events for all registered2488 * ssam_notifier_disable_registered() - Disable events for all registered
2279 * notifiers.2489 * notifiers.
drivers/platform/surface/aggregator/controller.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * Main SSAM/SSH controller structure and functionality.3 * Main SSAM/SSH controller structure and functionality.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#ifndef _SURFACE_AGGREGATOR_CONTROLLER_H8#ifndef _SURFACE_AGGREGATOR_CONTROLLER_H
drivers/platform/surface/aggregator/core.c+1−1
@@ -7,7 +7,7 @@ · 6 unmodified lines
7 * Handles communication via requests as well as enabling, disabling, and7 * Handles communication via requests as well as enabling, disabling, and
8 * relaying of events.8 * relaying of events.
9 *9 *
10 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>10 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
11 */11 */
12 12
13#include <linux/acpi.h>13#include <linux/acpi.h>
drivers/platform/surface/aggregator/ssh_msgb.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH message builder functions.3 * SSH message builder functions.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H8#ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H
drivers/platform/surface/aggregator/ssh_packet_layer.c+4−8
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH packet transport layer.3 * SSH packet transport layer.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#include <asm/unaligned.h>8#include <asm/unaligned.h>
@@ -1567,9 +1567,7 @@ static void ssh_ptl_timeout_reap(struct work_struct *work) · 1558 unmodified lines
1567 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);1567 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);
1568 1568
1569 atomic_dec(&ptl->pending.count);1569 atomic_dec(&ptl->pending.count);
1570 list_del(&p->pending_node);1570 list_move_tail(&p->pending_node, &claimed);
1571
1572 list_add_tail(&p->pending_node, &claimed);
1573 }1571 }
1574 1572
1575 spin_unlock(&ptl->pending.lock);1573 spin_unlock(&ptl->pending.lock);
@@ -1957,8 +1955,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl) · 381 unmodified lines
1957 smp_mb__before_atomic();1955 smp_mb__before_atomic();
1958 clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state);1956 clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state);
1959 1957
1960 list_del(&p->queue_node);1958 list_move_tail(&p->queue_node, &complete_q);
1961 list_add_tail(&p->queue_node, &complete_q);
1962 }1959 }
1963 spin_unlock(&ptl->queue.lock);1960 spin_unlock(&ptl->queue.lock);
1964 1961
@@ -1970,8 +1967,7 @@ void ssh_ptl_shutdown(struct ssh_ptl *ptl) · 5 unmodified lines
1970 smp_mb__before_atomic();1967 smp_mb__before_atomic();
1971 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);1968 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state);
1972 1969
1973 list_del(&p->pending_node);1970 list_move_tail(&p->pending_node, &complete_q);
1974 list_add_tail(&p->pending_node, &complete_q);
1975 }1971 }
1976 atomic_set(&ptl->pending.count, 0);1972 atomic_set(&ptl->pending.count, 0);
1977 spin_unlock(&ptl->pending.lock);1973 spin_unlock(&ptl->pending.lock);
drivers/platform/surface/aggregator/ssh_packet_layer.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH packet transport layer.3 * SSH packet transport layer.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H8#ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H
drivers/platform/surface/aggregator/ssh_parser.c+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH message parser.3 * SSH message parser.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#include <asm/unaligned.h>8#include <asm/unaligned.h>
drivers/platform/surface/aggregator/ssh_parser.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH message parser.3 * SSH message parser.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H8#ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H
drivers/platform/surface/aggregator/ssh_request_layer.c+4−8
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH request transport layer.3 * SSH request transport layer.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#include <asm/unaligned.h>8#include <asm/unaligned.h>
@@ -863,9 +863,7 @@ static void ssh_rtl_timeout_reap(struct work_struct *work) · 854 unmodified lines
863 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);863 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);
864 864
865 atomic_dec(&rtl->pending.count);865 atomic_dec(&rtl->pending.count);
866 list_del(&r->node);866 list_move_tail(&r->node, &claimed);
867
868 list_add_tail(&r->node, &claimed);
869 }867 }
870 spin_unlock(&rtl->pending.lock);868 spin_unlock(&rtl->pending.lock);
871 869
@@ -1204,8 +1202,7 @@ void ssh_rtl_shutdown(struct ssh_rtl *rtl) · 332 unmodified lines
1204 smp_mb__before_atomic();1202 smp_mb__before_atomic();
1205 clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state);1203 clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state);
1206 1204
1207 list_del(&r->node);1205 list_move_tail(&r->node, &claimed);
1208 list_add_tail(&r->node, &claimed);
1209 }1206 }
1210 spin_unlock(&rtl->queue.lock);1207 spin_unlock(&rtl->queue.lock);
1211 1208
@@ -1238,8 +1235,7 @@ void ssh_rtl_shutdown(struct ssh_rtl *rtl) · 26 unmodified lines
1238 smp_mb__before_atomic();1235 smp_mb__before_atomic();
1239 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);1236 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state);
1240 1237
1241 list_del(&r->node);1238 list_move_tail(&r->node, &claimed);
1242 list_add_tail(&r->node, &claimed);
1243 }1239 }
1244 spin_unlock(&rtl->pending.lock);1240 spin_unlock(&rtl->pending.lock);
1245 }1241 }
drivers/platform/surface/aggregator/ssh_request_layer.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * SSH request transport layer.3 * SSH request transport layer.
4 *4 *
5 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H8#ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H
drivers/platform/surface/aggregator/trace.h+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
2/*2/*
3 * Trace points for SSAM/SSH.3 * Trace points for SSAM/SSH.
4 *4 *
5 * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>5 * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
6 */6 */
7 7
8#undef TRACE_SYSTEM8#undef TRACE_SYSTEM
drivers/platform/surface/surface_aggregator_cdev.c+511−23
@@ -3,29 +3,69 @@ · 2 unmodified lines
3 * Provides user-space access to the SSAM EC via the /dev/surface/aggregator3 * Provides user-space access to the SSAM EC via the /dev/surface/aggregator
4 * misc device. Intended for debugging and development.4 * misc device. Intended for debugging and development.
5 *5 *
6 * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>6 * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
7 */7 */
8 8
9#include <linux/fs.h>9#include <linux/fs.h>
10#include <linux/ioctl.h>
10#include <linux/kernel.h>11#include <linux/kernel.h>
12#include <linux/kfifo.h>
11#include <linux/kref.h>13#include <linux/kref.h>
12#include <linux/miscdevice.h>14#include <linux/miscdevice.h>
13#include <linux/module.h>15#include <linux/module.h>
14#include <linux/platform_device.h>16#include <linux/platform_device.h>
17#include <linux/poll.h>
15#include <linux/rwsem.h>18#include <linux/rwsem.h>
16#include <linux/slab.h>19#include <linux/slab.h>
17#include <linux/uaccess.h>20#include <linux/uaccess.h>
21#include <linux/vmalloc.h>
18 22
19#include <linux/surface_aggregator/cdev.h>23#include <linux/surface_aggregator/cdev.h>
20#include <linux/surface_aggregator/controller.h>24#include <linux/surface_aggregator/controller.h>
25#include <linux/surface_aggregator/serial_hub.h>
21 26
22#define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev"27#define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev"
23 28
29
30/* -- Main structures. ------------------------------------------------------ */
31
32enum ssam_cdev_device_state {
33 SSAM_CDEV_DEVICE_SHUTDOWN_BIT = BIT(0),
34};
35
24struct ssam_cdev {36struct ssam_cdev {
25 struct kref kref;37 struct kref kref;
26 struct rw_semaphore lock;38 struct rw_semaphore lock;
39
40 struct device *dev;
27 struct ssam_controller *ctrl;41 struct ssam_controller *ctrl;
28 struct miscdevice mdev;42 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
49struct ssam_cdev_client;
50
51struct ssam_cdev_notifier {
52 struct ssam_cdev_client *client;
53 struct ssam_event_notifier nf;
54};
55
56struct 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;
29};69};
30 70
31static void __ssam_cdev_release(struct kref *kref)71static void __ssam_cdev_release(struct kref *kref)
@@ -47,24 +87,173 @@ static void ssam_cdev_put(struct ssam_cdev *cdev) · 15 unmodified lines
47 kref_put(&cdev->kref, __ssam_cdev_release);87 kref_put(&cdev->kref, __ssam_cdev_release);
48}88}
49 89
50static int ssam_cdev_device_open(struct inode *inode, struct file *filp)90
91/* -- Notifier handling. ---------------------------------------------------- */
92
93static u32 ssam_cdev_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in)
51{94{
52 struct miscdevice *mdev = filp->private_data;95 struct ssam_cdev_notifier *cdev_nf = container_of(nf, struct ssam_cdev_notifier, nf);
53 struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev);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);
54 127
55 filp->private_data = ssam_cdev_get(cdev);128 /*
56 return stream_open(inode, filp);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;
57}133}
58 134
59static int ssam_cdev_device_release(struct inode *inode, struct file *filp)135static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, int priority)
60{136{
61 ssam_cdev_put(filp->private_data);137 const u16 rqid = ssh_tc_to_rqid(tc);
62 return 0;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
187static 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
216static 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);
63}250}
64 251
65static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg)252
253/* -- IOCTL functions. ------------------------------------------------------ */
254
255static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_request __user *r)
66{256{
67 struct ssam_cdev_request __user *r;
68 struct ssam_cdev_request rqst;257 struct ssam_cdev_request rqst;
69 struct ssam_request spec = {};258 struct ssam_request spec = {};
70 struct ssam_response rsp = {};259 struct ssam_response rsp = {};
@@ -72,7 +261,8 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg) · 1 unmodified line
72 void __user *rspdata;261 void __user *rspdata;
73 int status = 0, ret = 0, tmp;262 int status = 0, ret = 0, tmp;
74 263
75 r = (struct ssam_cdev_request __user *)arg;264 lockdep_assert_held_read(&client->cdev->lock);
265
76 ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r));266 ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r));
77 if (ret)267 if (ret)
78 goto out;268 goto out;
@@ -152,7 +342,7 @@ static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg) · 73 unmodified lines
152 }342 }
153 343
154 /* Perform request. */344 /* Perform request. */
155 status = ssam_request_sync(cdev->ctrl, &spec, &rsp);345 status = ssam_request_sync(client->cdev->ctrl, &spec, &rsp);
156 if (status)346 if (status)
157 goto out;347 goto out;
158 348
@@ -177,48 +367,315 @@ out: · 18 unmodified lines
177 return ret;367 return ret;
178}368}
179 369
180static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd,370static 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
385static 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
400static 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
428static 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
459static 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
503static 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
527static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd,
181 unsigned long arg)528 unsigned long arg)
182{529{
530 lockdep_assert_held_read(&client->cdev->lock);
531
183 switch (cmd) {532 switch (cmd) {
184 case SSAM_CDEV_REQUEST:533 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);
186 549
187 default:550 default:
188 return -ENOTTY;551 return -ENOTTY;
189 }552 }
190}553}
191 554
192static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd,555static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
193 unsigned long arg)
194{556{
195 struct ssam_cdev *cdev = file->private_data;557 struct ssam_cdev_client *client = file->private_data;
196 long status;558 long status;
197 559
198 /* Ensure that controller is valid for as long as we need it. */560 /* 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
575static 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
199 if (down_read_killable(&cdev->lock))582 if (down_read_killable(&cdev->lock))
200 return -ERESTARTSYS;583 return -ERESTARTSYS;
201 584
202 if (!cdev->ctrl) {585 /* Make sure we're not shut down. */
586 if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) {
203 up_read(&cdev->lock);587 up_read(&cdev->lock);
204 return -ENODEV;588 return -ENODEV;
205 }589 }
206 590
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);
208 636
209 up_read(&cdev->lock);637 up_read(&cdev->lock);
210 return status;638 return copied;
639}
640
641static __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
657static 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);
211}662}
212 663
213static const struct file_operations ssam_controller_fops = {664static const struct file_operations ssam_controller_fops = {
214 .owner = THIS_MODULE,665 .owner = THIS_MODULE,
215 .open = ssam_cdev_device_open,666 .open = ssam_cdev_device_open,
216 .release = ssam_cdev_device_release,667 .release = ssam_cdev_device_release,
668 .read = ssam_cdev_read,
669 .poll = ssam_cdev_poll,
670 .fasync = ssam_cdev_fasync,
217 .unlocked_ioctl = ssam_cdev_device_ioctl,671 .unlocked_ioctl = ssam_cdev_device_ioctl,
218 .compat_ioctl = ssam_cdev_device_ioctl,672 .compat_ioctl = ssam_cdev_device_ioctl,
219 .llseek = noop_llseek,673 .llseek = no_llseek,
220};674};
221 675
676
677/* -- Device and driver setup ----------------------------------------------- */
678
222static int ssam_dbg_device_probe(struct platform_device *pdev)679static int ssam_dbg_device_probe(struct platform_device *pdev)
223{680{
224 struct ssam_controller *ctrl;681 struct ssam_controller *ctrl;
@@ -236,6 +693,7 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) · 11 unmodified lines
236 kref_init(&cdev->kref);693 kref_init(&cdev->kref);
237 init_rwsem(&cdev->lock);694 init_rwsem(&cdev->lock);
238 cdev->ctrl = ctrl;695 cdev->ctrl = ctrl;
696 cdev->dev = &pdev->dev;
239 697
240 cdev->mdev.parent = &pdev->dev;698 cdev->mdev.parent = &pdev->dev;
241 cdev->mdev.minor = MISC_DYNAMIC_MINOR;699 cdev->mdev.minor = MISC_DYNAMIC_MINOR;
@@ -243,6 +701,9 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) · 1 unmodified line
243 cdev->mdev.nodename = "surface/aggregator";701 cdev->mdev.nodename = "surface/aggregator";
244 cdev->mdev.fops = &ssam_controller_fops;702 cdev->mdev.fops = &ssam_controller_fops;
245 703
704 init_rwsem(&cdev->client_lock);
705 INIT_LIST_HEAD(&cdev->client_list);
706
246 status = misc_register(&cdev->mdev);707 status = misc_register(&cdev->mdev);
247 if (status) {708 if (status) {
248 kfree(cdev);709 kfree(cdev);
@@ -256,8 +717,32 @@ static int ssam_dbg_device_probe(struct platform_device *pdev) · 7 unmodified lines
256static int ssam_dbg_device_remove(struct platform_device *pdev)717static int ssam_dbg_device_remove(struct platform_device *pdev)
257{718{
258 struct ssam_cdev *cdev = platform_get_drvdata(pdev);719 struct ssam_cdev *cdev = platform_get_drvdata(pdev);
720 struct ssam_cdev_client *client;
259 721
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);
261 746
262 /*747 /*
263 * The controller is only guaranteed to be valid for as long as the748 * 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
266 */751 */
267 down_write(&cdev->lock);752 down_write(&cdev->lock);
268 cdev->ctrl = NULL;753 cdev->ctrl = NULL;
754 cdev->dev = NULL;
269 up_write(&cdev->lock);755 up_write(&cdev->lock);
270 756
757 misc_deregister(&cdev->mdev);
758
271 ssam_cdev_put(cdev);759 ssam_cdev_put(cdev);
272 return 0;760 return 0;
273}761}
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
119 .parent = &ssam_node_hub_base,119 .parent = &ssam_node_hub_base,
120};120};
121 121
122/* Devices for Surface Book 2. */122/*
123static const struct software_node *ssam_node_group_sb2[] = {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 */
128static const struct software_node *ssam_node_group_gen5[] = {
124 &ssam_node_root,129 &ssam_node_root,
125 &ssam_node_tmp_pprof,130 &ssam_node_tmp_pprof,
126 NULL,131 NULL,
@@ -142,20 +147,6 @@ static const struct software_node *ssam_node_group_sb3[] = { · 15 unmodified lines
142 NULL,147 NULL,
143};148};
144 149
145/* Devices for Surface Laptop 1. */
146static 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. */
153static const struct software_node *ssam_node_group_sl2[] = {
154 &ssam_node_root,
155 &ssam_node_tmp_pprof,
156 NULL,
157};
158
159/* Devices for Surface Laptop 3 and 4. */150/* Devices for Surface Laptop 3 and 4. */
160static const struct software_node *ssam_node_group_sl3[] = {151static const struct software_node *ssam_node_group_sl3[] = {
161 &ssam_node_root,152 &ssam_node_root,
@@ -177,20 +168,6 @@ static const struct software_node *ssam_node_group_slg1[] = { · 15 unmodified lines
177 NULL,168 NULL,
178};169};
179 170
180/* Devices for Surface Pro 5. */
181static 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. */
188static const struct software_node *ssam_node_group_sp6[] = {
189 &ssam_node_root,
190 &ssam_node_tmp_pprof,
191 NULL,
192};
193
194/* Devices for Surface Pro 7 and Surface Pro 7+. */171/* Devices for Surface Pro 7 and Surface Pro 7+. */
195static const struct software_node *ssam_node_group_sp7[] = {172static const struct software_node *ssam_node_group_sp7[] = {
196 &ssam_node_root,173 &ssam_node_root,
@@ -495,10 +472,10 @@ static struct ssam_device_driver ssam_base_hub_driver = { · 298 unmodified lines
495 472
496static const struct acpi_device_id ssam_platform_hub_match[] = {473static const struct acpi_device_id ssam_platform_hub_match[] = {
497 /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */474 /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
498 { "MSHW0081", (unsigned long)ssam_node_group_sp5 },475 { "MSHW0081", (unsigned long)ssam_node_group_gen5 },
499 476
500 /* Surface Pro 6 (OMBR >= 0x10) */477 /* Surface Pro 6 (OMBR >= 0x10) */
501 { "MSHW0111", (unsigned long)ssam_node_group_sp6 },478 { "MSHW0111", (unsigned long)ssam_node_group_gen5 },
502 479
503 /* Surface Pro 7 */480 /* Surface Pro 7 */
504 { "MSHW0116", (unsigned long)ssam_node_group_sp7 },481 { "MSHW0116", (unsigned long)ssam_node_group_sp7 },
@@ -507,16 +484,16 @@ static const struct acpi_device_id ssam_platform_hub_match[] = { · 2 unmodified lines
507 { "MSHW0119", (unsigned long)ssam_node_group_sp7 },484 { "MSHW0119", (unsigned long)ssam_node_group_sp7 },
508 485
509 /* Surface Book 2 */486 /* Surface Book 2 */
510 { "MSHW0107", (unsigned long)ssam_node_group_sb2 },487 { "MSHW0107", (unsigned long)ssam_node_group_gen5 },
511 488
512 /* Surface Book 3 */489 /* Surface Book 3 */
513 { "MSHW0117", (unsigned long)ssam_node_group_sb3 },490 { "MSHW0117", (unsigned long)ssam_node_group_sb3 },
514 491
515 /* Surface Laptop 1 */492 /* Surface Laptop 1 */
516 { "MSHW0086", (unsigned long)ssam_node_group_sl1 },493 { "MSHW0086", (unsigned long)ssam_node_group_gen5 },
517 494
518 /* Surface Laptop 2 */495 /* Surface Laptop 2 */
519 { "MSHW0112", (unsigned long)ssam_node_group_sl2 },496 { "MSHW0112", (unsigned long)ssam_node_group_gen5 },
520 497
521 /* Surface Laptop 3 (13", Intel) */498 /* Surface Laptop 3 (13", Intel) */
522 { "MSHW0114", (unsigned long)ssam_node_group_sl3 },499 { "MSHW0114", (unsigned long)ssam_node_group_sl3 },
drivers/platform/x86/Kconfig+21−28
@@ -415,16 +415,17 @@ config HP_ACCEL · 414 unmodified lines
415 To compile this driver as a module, choose M here: the module will415 To compile this driver as a module, choose M here: the module will
416 be called hp_accel.416 be called hp_accel.
417 417
418config HP_WIRELESS418config WIRELESS_HOTKEY
419 tristate "HP wireless button"419 tristate "Wireless hotkey button"
420 depends on ACPI420 depends on ACPI
421 depends on INPUT421 depends on INPUT
422 help422 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.
424 On such systems the driver should load automatically (via ACPI alias).425 On such systems the driver should load automatically (via ACPI alias).
425 426
426 To compile this driver as a module, choose M here: the module will427 To compile this driver as a module, choose M here: the module will
427 be called hp-wireless.428 be called wireless-hotkey.
428 429
429config HP_WMI430config HP_WMI
430 tristate "HP WMI extras"431 tristate "HP WMI extras"
@@ -639,6 +640,19 @@ config THINKPAD_ACPI_HOTKEY_POLL · 208 unmodified lines
639 If you are not sure, say Y here. The driver enables polling only if640 If you are not sure, say Y here. The driver enables polling only if
640 it is strictly necessary to do so.641 it is strictly necessary to do so.
641 642
643config 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
654source "drivers/platform/x86/intel/Kconfig"
655
642config INTEL_ATOMISP2_LED656config INTEL_ATOMISP2_LED
643 tristate "Intel AtomISP2 camera LED driver"657 tristate "Intel AtomISP2 camera LED driver"
644 depends on GPIOLIB && LEDS_GPIO658 depends on GPIOLIB && LEDS_GPIO
@@ -673,30 +687,6 @@ config INTEL_ATOMISP2_PM · 28 unmodified lines
673 To compile this driver as a module, choose M here: the module687 To compile this driver as a module, choose M here: the module
674 will be called intel_atomisp2_pm.688 will be called intel_atomisp2_pm.
675 689
676config 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
700config INTEL_HID_EVENT690config INTEL_HID_EVENT
701 tristate "INTEL HID Event"691 tristate "INTEL HID Event"
702 depends on ACPI692 depends on ACPI
@@ -1076,6 +1066,9 @@ config TOUCHSCREEN_DMI · 373 unmodified lines
1076 the OS-image for the device. This option supplies the missing info.1066 the OS-image for the device. This option supplies the missing info.
1077 Enable this for x86 tablets with Silead or Chipone touchscreens.1067 Enable this for x86 tablets with Silead or Chipone touchscreens.
1078 1068
1069config FW_ATTR_CLASS
1070 tristate
1071
1079config INTEL_IMR1072config INTEL_IMR
1080 bool "Intel Isolated Memory Region support"1073 bool "Intel Isolated Memory Region support"
1081 depends on X86_INTEL_QUARK && IOSF_MBI1074 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
52 52
53# Hewlett Packard53# Hewlett Packard
54obj-$(CONFIG_HP_ACCEL) += hp_accel.o54obj-$(CONFIG_HP_ACCEL) += hp_accel.o
55obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o
56obj-$(CONFIG_HP_WMI) += hp-wmi.o55obj-$(CONFIG_HP_WMI) += hp-wmi.o
57obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o56obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
58 57
@@ -64,14 +63,13 @@ obj-$(CONFIG_IBM_RTL) += ibm_rtl.o · 5 unmodified lines
64obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o63obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
65obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o64obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
66obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o65obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
66obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
67 67
68# Intel68# Intel
69obj-$(CONFIG_X86_PLATFORM_DRIVERS_INTEL) += intel/
70
69obj-$(CONFIG_INTEL_ATOMISP2_LED) += intel_atomisp2_led.o71obj-$(CONFIG_INTEL_ATOMISP2_LED) += intel_atomisp2_led.o
70obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o72obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
71obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
72intel_cht_int33fe-objs := intel_cht_int33fe_common.o \
73 intel_cht_int33fe_typec.o \
74 intel_cht_int33fe_microb.o
75obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o73obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
76obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o74obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
77obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o75obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
@@ -112,9 +110,11 @@ obj-$(CONFIG_SYSTEM76_ACPI) += system76_acpi.o · 34 unmodified lines
112obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o110obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
113 111
114# Platform drivers112# Platform drivers
113obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o
115obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o114obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
116obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o115obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
117obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o116obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
117obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
118 118
119# Intel uncore drivers119# Intel uncore drivers
120obj-$(CONFIG_INTEL_IPS) += intel_ips.o120obj-$(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
110 .wmi_force_als_set = true,110 .wmi_force_als_set = true,
111};111};
112 112
113static struct quirk_entry quirk_asus_vendor_backlight = {
114 .wmi_backlight_power = true,
115 .wmi_backlight_set_devstate = true,
116};
117
118static struct quirk_entry quirk_asus_use_kbd_dock_devid = {113static struct quirk_entry quirk_asus_use_kbd_dock_devid = {
119 .use_kbd_dock_devid = true,114 .use_kbd_dock_devid = true,
120};115};
@@ -425,78 +420,6 @@ static const struct dmi_system_id asus_quirks[] = { · 304 unmodified lines
425 },420 },
426 .driver_data = &quirk_asus_forceals,421 .driver_data = &quirk_asus_forceals,
427 },422 },
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 },
500 {423 {
501 .callback = dmi_matched,424 .callback = dmi_matched,
502 .ident = "Asus Transformer T100TA / T100HA / T100CHI",425 .ident = "Asus Transformer T100TA / T100HA / T100CHI",
drivers/platform/x86/dell/Kconfig+10−1
@@ -5,7 +5,6 @@ · 4 unmodified lines
5 5
6menuconfig X86_PLATFORM_DRIVERS_DELL6menuconfig X86_PLATFORM_DRIVERS_DELL
7 bool "Dell X86 Platform Specific Device Drivers"7 bool "Dell X86 Platform Specific Device Drivers"
8 default n
9 depends on X86_PLATFORM_DEVICES8 depends on X86_PLATFORM_DEVICES
10 help9 help
11 Say Y here to get to see options for device drivers for various10 Say Y here to get to see options for device drivers for various
@@ -53,6 +52,7 @@ config DELL_LAPTOP · 41 unmodified lines
53 depends on BACKLIGHT_CLASS_DEVICE52 depends on BACKLIGHT_CLASS_DEVICE
54 depends on ACPI_VIDEO || ACPI_VIDEO = n53 depends on ACPI_VIDEO || ACPI_VIDEO = n
55 depends on RFKILL || RFKILL = n54 depends on RFKILL || RFKILL = n
55 depends on DELL_WMI || DELL_WMI = n
56 depends on SERIO_I804256 depends on SERIO_I8042
57 depends on DELL_SMBIOS57 depends on DELL_SMBIOS
58 select POWER_SUPPLY58 select POWER_SUPPLY
@@ -164,6 +164,14 @@ config DELL_WMI · 105 unmodified lines
164 To compile this driver as a module, choose M here: the module will164 To compile this driver as a module, choose M here: the module will
165 be called dell-wmi.165 be called dell-wmi.
166 166
167config 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
167config DELL_WMI_AIO175config DELL_WMI_AIO
168 tristate "WMI Hotkeys for Dell All-In-One series"176 tristate "WMI Hotkeys for Dell All-In-One series"
169 default m177 default m
@@ -197,6 +205,7 @@ config DELL_WMI_SYSMAN · 27 unmodified lines
197 depends on ACPI_WMI205 depends on ACPI_WMI
198 depends on DMI206 depends on DMI
199 select NLS207 select NLS
208 select FW_ATTR_CLASS
200 help209 help
201 This driver allows changing BIOS settings on many Dell machines from210 This driver allows changing BIOS settings on many Dell machines from
202 2018 and newer without the use of any additional software.211 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
15dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o15dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
16obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o16obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
17obj-$(CONFIG_DELL_WMI) += dell-wmi.o17obj-$(CONFIG_DELL_WMI) += dell-wmi.o
18dell-wmi-objs := dell-wmi-base.o
19dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o
18obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o20obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
19obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o21obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
20obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o22obj-$(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
394 394
395 /* wait a few to see if it executed */395 /* wait a few to see if it executed */
396 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;396 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
397 while ((cmd_status = inb(PCAT_APM_STATUS_PORT))397 while ((s8)inb(PCAT_APM_STATUS_PORT) == ESM_STATUS_CMD_UNSUCCESSFUL) {
398 == ESM_STATUS_CMD_UNSUCCESSFUL) {
399 num_ticks--;398 num_ticks--;
400 if (num_ticks == EXPIRED_TIMER)399 if (num_ticks == EXPIRED_TIMER)
401 return -ETIME;400 return -ETIME;
drivers/platform/x86/dell/dell-laptop.c+10−3
@@ -31,6 +31,8 @@ · 30 unmodified lines
31#include "dell-rbtn.h"31#include "dell-rbtn.h"
32#include "dell-smbios.h"32#include "dell-smbios.h"
33 33
34#include "dell-wmi-privacy.h"
35
34struct quirk_entry {36struct quirk_entry {
35 bool touchpad_led;37 bool touchpad_led;
36 bool kbd_led_not_present;38 bool kbd_led_not_present;
@@ -90,6 +92,7 @@ static struct rfkill *wifi_rfkill; · 53 unmodified lines
90static struct rfkill *bluetooth_rfkill;92static struct rfkill *bluetooth_rfkill;
91static struct rfkill *wwan_rfkill;93static struct rfkill *wwan_rfkill;
92static bool force_rfkill;94static bool force_rfkill;
95static bool micmute_led_registered;
93 96
94module_param(force_rfkill, bool, 0444);97module_param(force_rfkill, bool, 0444);
95MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");98MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
@@ -2205,11 +2208,13 @@ static int __init dell_init(void) · 2109 unmodified lines
2205 dell_laptop_register_notifier(&dell_laptop_notifier);2208 dell_laptop_register_notifier(&dell_laptop_notifier);
2206 2209
2207 if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&2210 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()) {
2209 micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);2213 micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
2210 ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);2214 ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
2211 if (ret < 0)2215 if (ret < 0)
2212 goto fail_led;2216 goto fail_led;
2217 micmute_led_registered = true;
2213 }2218 }
2214 2219
2215 if (acpi_video_get_backlight_type() != acpi_backlight_vendor)2220 if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
@@ -2257,7 +2262,8 @@ static int __init dell_init(void) · 41 unmodified lines
2257fail_get_brightness:2262fail_get_brightness:
2258 backlight_device_unregister(dell_backlight_device);2263 backlight_device_unregister(dell_backlight_device);
2259fail_backlight:2264fail_backlight:
2260 led_classdev_unregister(&micmute_led_cdev);2265 if (micmute_led_registered)
2266 led_classdev_unregister(&micmute_led_cdev);
2261fail_led:2267fail_led:
2262 dell_cleanup_rfkill();2268 dell_cleanup_rfkill();
2263fail_rfkill:2269fail_rfkill:
@@ -2278,7 +2284,8 @@ static void __exit dell_exit(void) · 14 unmodified lines
2278 touchpad_led_exit();2284 touchpad_led_exit();
2279 kbd_led_exit();2285 kbd_led_exit();
2280 backlight_device_unregister(dell_backlight_device);2286 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);
2282 dell_cleanup_rfkill();2289 dell_cleanup_rfkill();
2283 if (platform_device) {2290 if (platform_device) {
2284 platform_device_unregister(platform_device);2291 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
27#include <acpi/video.h>27#include <acpi/video.h>
28#include "dell-smbios.h"28#include "dell-smbios.h"
29#include "dell-wmi-descriptor.h"29#include "dell-wmi-descriptor.h"
30#include "dell-wmi-privacy.h"
30 31
31MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");32MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
32MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");33MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
@@ -427,7 +428,6 @@ static void dell_wmi_notify(struct wmi_device *wdev, · 394 unmodified lines
427 428
428 switch (buffer_entry[1]) {429 switch (buffer_entry[1]) {
429 case 0x0000: /* One key pressed or event occurred */430 case 0x0000: /* One key pressed or event occurred */
430 case 0x0012: /* Event with extended data occurred */
431 if (len > 2)431 if (len > 2)
432 dell_wmi_process_key(wdev, buffer_entry[1],432 dell_wmi_process_key(wdev, buffer_entry[1],
433 buffer_entry[2]);433 buffer_entry[2]);
@@ -439,6 +439,13 @@ static void dell_wmi_notify(struct wmi_device *wdev, · 5 unmodified lines
439 dell_wmi_process_key(wdev, buffer_entry[1],439 dell_wmi_process_key(wdev, buffer_entry[1],
440 buffer_entry[i]);440 buffer_entry[i]);
441 break;441 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;
442 default: /* Unknown event */449 default: /* Unknown event */
443 pr_info("Unknown WMI event type 0x%x\n",450 pr_info("Unknown WMI event type 0x%x\n",
444 (int)buffer_entry[1]);451 (int)buffer_entry[1]);
@@ -747,6 +754,10 @@ static int __init dell_wmi_init(void) · 302 unmodified lines
747 }754 }
748 }755 }
749 756
757 err = dell_privacy_register_driver();
758 if (err)
759 return err;
760
750 return wmi_driver_register(&dell_wmi_driver);761 return wmi_driver_register(&dell_wmi_driver);
751}762}
752late_initcall(dell_wmi_init);763late_initcall(dell_wmi_init);
@@ -757,6 +768,7 @@ static void __exit dell_wmi_exit(void) · 4 unmodified lines
757 dell_wmi_events_set_enabled(false);768 dell_wmi_events_set_enabled(false);
758 769
759 wmi_driver_unregister(&dell_wmi_driver);770 wmi_driver_unregister(&dell_wmi_driver);
771 dell_privacy_unregister_driver();
760}772}
761module_exit(dell_wmi_exit);773module_exit(dell_wmi_exit);
762 774
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 */
31static LIST_HEAD(wmi_list);
32static DEFINE_MUTEX(list_mutex);
33
34struct 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 */
44enum 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
51static 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 */
60static 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
68bool 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}
80EXPORT_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 */
95bool 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
127error:
128 mutex_unlock(&list_mutex);
129 return ret;
130}
131
132static 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
152static 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
174static DEVICE_ATTR_RO(dell_privacy_supported_type);
175static DEVICE_ATTR_RO(dell_privacy_current_state);
176
177static struct attribute *privacy_attributes[] = {
178 &dev_attr_dell_privacy_supported_type.attr,
179 &dev_attr_dell_privacy_current_state.attr,
180 NULL,
181};
182
183static 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 */
202static 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
238obj_free:
239 kfree(obj_present);
240 return ret;
241}
242
243static 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 */
282static 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
294static 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
360static 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
369static const struct wmi_device_id dell_wmi_privacy_wmi_id_table[] = {
370 { .guid_string = DELL_PRIVACY_GUID },
371 { },
372};
373
374static 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
383int dell_privacy_register_driver(void)
384{
385 return wmi_driver_register(&dell_privacy_wmi_driver);
386}
387
388void 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)
12bool dell_privacy_has_mic_mute(void);
13bool dell_privacy_process_event(int type, int code, int status);
14int dell_privacy_register_driver(void);
15void dell_privacy_unregister_driver(void);
16#else /* CONFIG_DELL_PRIVACY */
17static inline bool dell_privacy_has_mic_mute(void)
18{
19 return false;
20}
21
22static inline bool dell_privacy_process_event(int type, int code, int status)
23{
24 return false;
25}
26
27static inline int dell_privacy_register_driver(void)
28{
29 return 0;
30}
31
32static 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
152 return ret ? ret : count; \152 return ret ? ret : count; \
153}153}
154 154
155#define check_property_type(attr, prop, valuetype) \
156 (attr##_obj[prop].type != valuetype)
157
155union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string);158union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string);
156int get_instance_count(const char *guid_string);159int get_instance_count(const char *guid_string);
157void strlcpy_attr(char *dest, char *src);160void strlcpy_attr(char *dest, char *src);
158 161
159int populate_enum_data(union acpi_object *enumeration_obj, int instance_id,162int 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);
161int alloc_enum_data(void);164int alloc_enum_data(void);
162void exit_enum_attributes(void);165void exit_enum_attributes(void);
163 166
drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c+34−5
@@ -132,39 +132,68 @@ int alloc_enum_data(void) · 131 unmodified lines
132 * @enumeration_obj: ACPI object with enumeration data132 * @enumeration_obj: ACPI object with enumeration data
133 * @instance_id: The instance to enumerate133 * @instance_id: The instance to enumerate
134 * @attr_name_kobj: The parent kernel object134 * @attr_name_kobj: The parent kernel object
135 * @enum_property_count: Total properties count under enumeration type
135 */136 */
136int populate_enum_data(union acpi_object *enumeration_obj, int instance_id,137int 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)
138{139{
139 int i, next_obj, value_modifier_count, possible_values_count;140 int i, next_obj, value_modifier_count, possible_values_count;
140 141
141 wmi_priv.enumeration_data[instance_id].attr_name_kobj = attr_name_kobj;142 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;
142 strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name,145 strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name,
143 enumeration_obj[ATTR_NAME].string.pointer);146 enumeration_obj[ATTR_NAME].string.pointer);
147 if (check_property_type(enumeration, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
148 return -EINVAL;
144 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code,149 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code,
145 enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer);150 enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer);
151 if (check_property_type(enumeration, DISPLAY_NAME, ACPI_TYPE_STRING))
152 return -EINVAL;
146 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name,153 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name,
147 enumeration_obj[DISPLAY_NAME].string.pointer);154 enumeration_obj[DISPLAY_NAME].string.pointer);
155 if (check_property_type(enumeration, DEFAULT_VAL, ACPI_TYPE_STRING))
156 return -EINVAL;
148 strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value,157 strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value,
149 enumeration_obj[DEFAULT_VAL].string.pointer);158 enumeration_obj[DEFAULT_VAL].string.pointer);
159 if (check_property_type(enumeration, MODIFIER, ACPI_TYPE_STRING))
160 return -EINVAL;
150 strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier,161 strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier,
151 enumeration_obj[MODIFIER].string.pointer);162 enumeration_obj[MODIFIER].string.pointer);
152 163
153 next_obj = MODIFIER + 1;164 next_obj = MODIFIER + 1;
154 165
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;
156 172
157 for (i = 0; i < value_modifier_count; i++) {173 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;
158 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier,178 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier,
159 enumeration_obj[++next_obj].string.pointer);179 enumeration_obj[next_obj++].string.pointer);
160 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";");180 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";");
161 }181 }
162 182
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;
164 189
165 for (i = 0; i < possible_values_count; i++) {190 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;
166 strcat(wmi_priv.enumeration_data[instance_id].possible_values,195 strcat(wmi_priv.enumeration_data[instance_id].possible_values,
167 enumeration_obj[++next_obj].string.pointer);196 enumeration_obj[next_obj++].string.pointer);
168 strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";");197 strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";");
169 }198 }
170 199
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
141 struct kobject *attr_name_kobj)141 struct kobject *attr_name_kobj)
142{142{
143 wmi_priv.integer_data[instance_id].attr_name_kobj = attr_name_kobj;143 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;
144 strlcpy_attr(wmi_priv.integer_data[instance_id].attribute_name,146 strlcpy_attr(wmi_priv.integer_data[instance_id].attribute_name,
145 integer_obj[ATTR_NAME].string.pointer);147 integer_obj[ATTR_NAME].string.pointer);
148 if (check_property_type(integer, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
149 return -EINVAL;
146 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name_language_code,150 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name_language_code,
147 integer_obj[DISPL_NAME_LANG_CODE].string.pointer);151 integer_obj[DISPL_NAME_LANG_CODE].string.pointer);
152 if (check_property_type(integer, DISPLAY_NAME, ACPI_TYPE_STRING))
153 return -EINVAL;
148 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name,154 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name,
149 integer_obj[DISPLAY_NAME].string.pointer);155 integer_obj[DISPLAY_NAME].string.pointer);
156 if (check_property_type(integer, DEFAULT_VAL, ACPI_TYPE_INTEGER))
157 return -EINVAL;
150 wmi_priv.integer_data[instance_id].default_value =158 wmi_priv.integer_data[instance_id].default_value =
151 (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer;159 (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer;
160 if (check_property_type(integer, MODIFIER, ACPI_TYPE_STRING))
161 return -EINVAL;
152 strlcpy_attr(wmi_priv.integer_data[instance_id].dell_modifier,162 strlcpy_attr(wmi_priv.integer_data[instance_id].dell_modifier,
153 integer_obj[MODIFIER].string.pointer);163 integer_obj[MODIFIER].string.pointer);
164 if (check_property_type(integer, MIN_VALUE, ACPI_TYPE_INTEGER))
165 return -EINVAL;
154 wmi_priv.integer_data[instance_id].min_value =166 wmi_priv.integer_data[instance_id].min_value =
155 (uintptr_t)integer_obj[MIN_VALUE].string.pointer;167 (uintptr_t)integer_obj[MIN_VALUE].string.pointer;
168 if (check_property_type(integer, MAX_VALUE, ACPI_TYPE_INTEGER))
169 return -EINVAL;
156 wmi_priv.integer_data[instance_id].max_value =170 wmi_priv.integer_data[instance_id].max_value =
157 (uintptr_t)integer_obj[MAX_VALUE].string.pointer;171 (uintptr_t)integer_obj[MAX_VALUE].string.pointer;
172 if (check_property_type(integer, SCALAR_INCR, ACPI_TYPE_INTEGER))
173 return -EINVAL;
158 wmi_priv.integer_data[instance_id].scalar_increment =174 wmi_priv.integer_data[instance_id].scalar_increment =
159 (uintptr_t)integer_obj[SCALAR_INCR].string.pointer;175 (uintptr_t)integer_obj[SCALAR_INCR].string.pointer;
160 176
drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c+6−0
@@ -159,10 +159,16 @@ int alloc_po_data(void) · 158 unmodified lines
159int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj)159int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj)
160{160{
161 wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj;161 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;
162 strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name,164 strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name,
163 po_obj[ATTR_NAME].string.pointer);165 po_obj[ATTR_NAME].string.pointer);
166 if (check_property_type(po, MIN_PASS_LEN, ACPI_TYPE_INTEGER))
167 return -EINVAL;
164 wmi_priv.po_data[instance_id].min_password_length =168 wmi_priv.po_data[instance_id].min_password_length =
165 (uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;169 (uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;
170 if (check_property_type(po, MAX_PASS_LEN, ACPI_TYPE_INTEGER))
171 return -EINVAL;
166 wmi_priv.po_data[instance_id].max_password_length =172 wmi_priv.po_data[instance_id].max_password_length =
167 (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;173 (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;
168 174
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
95 95
96 print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size);96 print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
97 ret = call_password_interface(wmi_priv.password_attr_wdev, buffer, buffer_size);97 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 */
99 if (!ret)99 if (!ret)
100 memset(current_password, 0, MAX_BUFF);100 strscpy(current_password, new, MAX_BUFF);
101 /* explain to user the detailed failure reason */101 /* explain to user the detailed failure reason */
102 else if (ret == -EOPNOTSUPP)102 else if (ret == -EOPNOTSUPP)
103 dev_err(&wmi_priv.password_attr_wdev->dev, "admin password must be configured\n");103 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
118 118
119/**119/**
120 * populate_str_data() - Populate all properties of an instance under string attribute120 * populate_str_data() - Populate all properties of an instance under string attribute
121 * @str_obj: ACPI object with integer data121 * @str_obj: ACPI object with string data
122 * @instance_id: The instance to enumerate122 * @instance_id: The instance to enumerate
123 * @attr_name_kobj: The parent kernel object123 * @attr_name_kobj: The parent kernel object
124 */124 */
125int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj)125int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj)
126{126{
127 wmi_priv.str_data[instance_id].attr_name_kobj = attr_name_kobj;127 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;
128 strlcpy_attr(wmi_priv.str_data[instance_id].attribute_name,130 strlcpy_attr(wmi_priv.str_data[instance_id].attribute_name,
129 str_obj[ATTR_NAME].string.pointer);131 str_obj[ATTR_NAME].string.pointer);
132 if (check_property_type(str, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
133 return -EINVAL;
130 strlcpy_attr(wmi_priv.str_data[instance_id].display_name_language_code,134 strlcpy_attr(wmi_priv.str_data[instance_id].display_name_language_code,
131 str_obj[DISPL_NAME_LANG_CODE].string.pointer);135 str_obj[DISPL_NAME_LANG_CODE].string.pointer);
136 if (check_property_type(str, DISPLAY_NAME, ACPI_TYPE_STRING))
137 return -EINVAL;
132 strlcpy_attr(wmi_priv.str_data[instance_id].display_name,138 strlcpy_attr(wmi_priv.str_data[instance_id].display_name,
133 str_obj[DISPLAY_NAME].string.pointer);139 str_obj[DISPLAY_NAME].string.pointer);
140 if (check_property_type(str, DEFAULT_VAL, ACPI_TYPE_STRING))
141 return -EINVAL;
134 strlcpy_attr(wmi_priv.str_data[instance_id].default_value,142 strlcpy_attr(wmi_priv.str_data[instance_id].default_value,
135 str_obj[DEFAULT_VAL].string.pointer);143 str_obj[DEFAULT_VAL].string.pointer);
144 if (check_property_type(str, MODIFIER, ACPI_TYPE_STRING))
145 return -EINVAL;
136 strlcpy_attr(wmi_priv.str_data[instance_id].dell_modifier,146 strlcpy_attr(wmi_priv.str_data[instance_id].dell_modifier,
137 str_obj[MODIFIER].string.pointer);147 str_obj[MODIFIER].string.pointer);
148 if (check_property_type(str, MIN_LEN, ACPI_TYPE_INTEGER))
149 return -EINVAL;
138 wmi_priv.str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer;150 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;
139 wmi_priv.str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer;153 wmi_priv.str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer;
140 154
141 return sysfs_create_group(attr_name_kobj, &str_attr_group);155 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
13#include <linux/kernel.h>13#include <linux/kernel.h>
14#include <linux/wmi.h>14#include <linux/wmi.h>
15#include "dell-wmi-sysman.h"15#include "dell-wmi-sysman.h"
16#include "../../firmware_attributes_class.h"
16 17
17#define MAX_TYPES 418#define MAX_TYPES 4
18#include <linux/nls.h>19#include <linux/nls.h>
19 20
20static struct class firmware_attributes_class = {
21 .name = "firmware-attributes",
22};
23
24struct wmi_sysman_priv wmi_priv = {21struct wmi_sysman_priv wmi_priv = {
25 .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex),22 .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex),
26};23};
@@ -28,6 +25,7 @@ struct wmi_sysman_priv wmi_priv = { · 1 unmodified line
28/* reset bios to defaults */25/* reset bios to defaults */
29static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};26static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
30static int reset_option = -1;27static int reset_option = -1;
28static struct class *fw_attr_class;
31 29
32 30
33/**31/**
@@ -481,7 +479,8 @@ static int init_bios_attributes(int attr_type, const char *guid) · 447 unmodified lines
481 /* enumerate all of this attribute */479 /* enumerate all of this attribute */
482 switch (attr_type) {480 switch (attr_type) {
483 case ENUM:481 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);
485 break;484 break;
486 case INT:485 case INT:
487 retval = populate_int_data(elements, instance_id, attr_name_kobj);486 retval = populate_int_data(elements, instance_id, attr_name_kobj);
@@ -541,11 +540,11 @@ static int __init sysman_init(void) · 53 unmodified lines
541 goto err_exit_bios_attr_pass_interface;540 goto err_exit_bios_attr_pass_interface;
542 }541 }
543 542
544 ret = class_register(&firmware_attributes_class);543 ret = fw_attributes_class_get(&fw_attr_class);
545 if (ret)544 if (ret)
546 goto err_exit_bios_attr_pass_interface;545 goto err_exit_bios_attr_pass_interface;
547 546
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),
549 NULL, "%s", DRIVER_NAME);548 NULL, "%s", DRIVER_NAME);
550 if (IS_ERR(wmi_priv.class_dev)) {549 if (IS_ERR(wmi_priv.class_dev)) {
551 ret = PTR_ERR(wmi_priv.class_dev);550 ret = PTR_ERR(wmi_priv.class_dev);
@@ -602,10 +601,10 @@ err_release_attributes_data: · 50 unmodified lines
602 release_attributes_data();601 release_attributes_data();
603 602
604err_destroy_classdev:603err_destroy_classdev:
605 device_destroy(&firmware_attributes_class, MKDEV(0, 0));604 device_destroy(fw_attr_class, MKDEV(0, 0));
606 605
607err_unregister_class:606err_unregister_class:
608 class_unregister(&firmware_attributes_class);607 fw_attributes_class_put();
609 608
610err_exit_bios_attr_pass_interface:609err_exit_bios_attr_pass_interface:
611 exit_bios_attr_pass_interface();610 exit_bios_attr_pass_interface();
@@ -619,8 +618,8 @@ err_exit_bios_attr_set_interface: · 7 unmodified lines
619static void __exit sysman_exit(void)618static void __exit sysman_exit(void)
620{619{
621 release_attributes_data();620 release_attributes_data();
622 device_destroy(&firmware_attributes_class, MKDEV(0, 0));621 device_destroy(fw_attr_class, MKDEV(0, 0));
623 class_unregister(&firmware_attributes_class);622 fw_attributes_class_put();
624 exit_bios_attr_set_interface();623 exit_bios_attr_set_interface();
625 exit_bios_attr_pass_interface();624 exit_bios_attr_pass_interface();
626}625}
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
10static DEFINE_MUTEX(fw_attr_lock);
11static int fw_attr_inuse;
12
13static struct class firmware_attributes_class = {
14 .name = "firmware-attributes",
15};
16
17int 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}
34EXPORT_SYMBOL_GPL(fw_attributes_class_get);
35
36int 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}
49EXPORT_SYMBOL_GPL(fw_attributes_class_put);
50
51MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
52MODULE_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
8int fw_attributes_class_get(struct class **fw_attr_class);
9int 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
462 NULL,462 NULL,
463};463};
464 464
465static struct attribute_group hdaps_attribute_group = {465static const struct attribute_group hdaps_attribute_group = {
466 .attrs = hdaps_attributes,466 .attrs = hdaps_attributes,
467};467};
468 468
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
16MODULE_LICENSE("GPL");
17MODULE_AUTHOR("Alex Hung");
18MODULE_ALIAS("acpi*:HPQ6001:*");
19MODULE_ALIAS("acpi*:WSTADEF:*");
20MODULE_ALIAS("acpi*:AMDI0051:*");
21
22static struct input_dev *hpwl_input_dev;
23
24static const struct acpi_device_id hpwl_ids[] = {
25 {"HPQ6001", 0},
26 {"WSTADEF", 0},
27 {"AMDI0051", 0},
28 {"", 0},
29};
30
31static 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
51err_free_dev:
52 input_free_device(hpwl_input_dev);
53 return err;
54}
55
56static void hp_wireless_input_destroy(void)
57{
58 input_unregister_device(hpwl_input_dev);
59}
60
61static 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
74static 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
85static int hpwl_remove(struct acpi_device *device)
86{
87 hp_wireless_input_destroy();
88 return 0;
89}
90
91static 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
102module_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
1408 case 6:1408 case 6:
1409 ideapad_input_report(priv, bit);1409 ideapad_input_report(priv, bit);
1410 break;1410 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;
1411 case 9:1423 case 9:
1412 ideapad_sync_rfk_state(priv);1424 ideapad_sync_rfk_state(priv);
1413 break;1425 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
6menuconfig 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
17if X86_PLATFORM_DRIVERS_INTEL
18
19source "drivers/platform/x86/intel/int33fe/Kconfig"
20source "drivers/platform/x86/intel/int3472/Kconfig"
21
22endif # 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
7obj-$(CONFIG_INTEL_CHT_INT33FE) += int33fe/
8obj-$(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
2config 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
2obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
3intel_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
168 return -ENODEV;168 return -ENODEV;
169 }169 }
170 170
171 /* Then the DP child device node */171 /* Then the DP-2 child device node */
172 data->dp = device_get_named_child_node(&pdev->dev, "DD02");172 data->dp = device_get_named_child_node(&pdev->dev, "DD04");
173 pci_dev_put(pdev);173 pci_dev_put(pdev);
174 if (!data->dp)174 if (!data->dp)
175 return -ENODEV;175 return -ENODEV;
drivers/platform/x86/intel/int3472/Kconfig+30−0
@@ -0,0 +1,30 @@
1config 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 @@
1obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472.o
2intel_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 */
19static const struct regulator_ops int3472_gpio_regulator_ops;
20
21static 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
31static 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
39static 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
50static void skl_int3472_clk_disable(struct clk_hw *hw)
51{
52 /* Likewise, nothing to do here... */
53}
54
55static 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
76static 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
84static 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
92int 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
125err_unregister_clk:
126 clk_unregister(int3472->clock.clk);
127out_free_init_name:
128 kfree(init.name);
129
130 return ret;
131}
132
133void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)
134{
135 clkdev_drop(int3472->clock.cl);
136 clk_unregister(int3472->clock.clk);
137}
138
139int 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
197err_free_gpio:
198 gpiod_put(int3472->regulator.gpio);
199
200 return ret;
201}
202
203void 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
11union 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
35int 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
53out_free_obj:
54 kfree(obj);
55 return ret;
56}
57
58static const struct acpi_device_id int3472_device_id[] = {
59 { "INT3472", 0 },
60 { }
61};
62MODULE_DEVICE_TABLE(acpi, int3472_device_id);
63
64static 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
73static 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
81static 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}
95module_init(skl_int3472_init);
96
97static void skl_int3472_exit(void)
98{
99 platform_driver_unregister(&int3472_discrete);
100 i2c_del_driver(&int3472_tps68470);
101}
102module_exit(skl_int3472_exit);
103
104MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver");
105MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
106MODULE_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
48struct acpi_device;
49struct i2c_client;
50struct platform_device;
51
52struct 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
67struct int3472_gpio_function_remap {
68 const char *documented;
69 const char *actual;
70};
71
72struct 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
78struct 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
108int skl_int3472_discrete_probe(struct platform_device *pdev);
109int skl_int3472_discrete_remove(struct platform_device *pdev);
110int skl_int3472_tps68470_probe(struct i2c_client *client);
111union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev,
112 char *id);
113int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb);
114
115int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
116void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
117
118int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
119 struct acpi_resource_gpio *agpio);
120void 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 */
26static 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 */
35static 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
52static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = {
53 { "reset", NULL },
54 { "powerdown", "reset" },
55 { }
56};
57
58static 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
67static const struct int3472_sensor_config *
68skl_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
105static 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
158static 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 */
220static 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
295static 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
335int 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
392err_put_sensor:
393 acpi_dev_put(int3472->sensor);
394
395 return ret;
396}
397
398int 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
15static const struct mfd_cell tps68470_cros[] = {
16 { .name = "tps68470-gpio" },
17 { .name = "tps68470_pmic_opregion" },
18};
19
20static const struct mfd_cell tps68470_win[] = {
21 { .name = "tps68470-gpio" },
22 { .name = "tps68470-clk" },
23 { .name = "tps68470-regulator" },
24};
25
26static const struct regmap_config tps68470_regmap_config = {
27 .reg_bits = 8,
28 .val_bits = 8,
29 .max_register = TPS68470_REG_MAX,
30};
31
32static 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 */
76static 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
98int 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
829 829
830static u16 read_mgtv(struct ips_driver *ips)830static u16 read_mgtv(struct ips_driver *ips)
831{831{
832 u16 ret;832 u16 __maybe_unused ret;
833 u64 slope, offset;833 u64 slope, offset;
834 u64 val;834 u64 val;
835 835
drivers/platform/x86/intel_pmt_crashlog.c+1−1
@@ -218,7 +218,7 @@ static struct attribute *pmt_crashlog_attrs[] = { · 217 unmodified lines
218 NULL218 NULL
219};219};
220 220
221static struct attribute_group pmt_crashlog_group = {221static const struct attribute_group pmt_crashlog_group = {
222 .attrs = pmt_crashlog_attrs,222 .attrs = pmt_crashlog_attrs,
223};223};
224 224
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
281struct isst_if_cpu_info {281struct isst_if_cpu_info {
282 /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */282 /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
283 int bus_info[2];283 int bus_info[2];
284 struct pci_dev *pci_dev[2];
284 int punit_cpu_id;285 int punit_cpu_id;
286 int numa_node;
285};287};
286 288
287static struct isst_if_cpu_info *isst_cpu_info;289static struct isst_if_cpu_info *isst_cpu_info;
290#define ISST_MAX_PCI_DOMAINS 8
291
292static 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}
288 347
289/**348/**
290 * isst_if_get_pci_dev() - Get the PCI device instance for a CPU349 * 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
300 */359 */
301struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)360struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
302{361{
303 int bus_number;362 struct pci_dev *pci_dev;
304 363
305 if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||364 if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
306 cpu >= num_possible_cpus())365 cpu >= num_possible_cpus())
307 return NULL;366 return NULL;
308 367
309 bus_number = isst_cpu_info[cpu].bus_info[bus_no];368 pci_dev = isst_cpu_info[cpu].pci_dev[bus_no];
310 if (bus_number < 0)369
311 return NULL;370 if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn))
371 return pci_dev;
312 372
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);
314}374}
315EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);375EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
316 376
@@ -327,6 +387,8 @@ static int isst_if_cpu_online(unsigned int cpu) · 10 unmodified lines
327 } else {387 } else {
328 isst_cpu_info[cpu].bus_info[0] = data & 0xff;388 isst_cpu_info[cpu].bus_info[0] = data & 0xff;
329 isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;389 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);
330 }392 }
331 393
332 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);394 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
@@ -335,6 +397,7 @@ static int isst_if_cpu_online(unsigned int cpu) · 2 unmodified lines
335 return ret;397 return ret;
336 }398 }
337 isst_cpu_info[cpu].punit_cpu_id = data;399 isst_cpu_info[cpu].punit_cpu_id = data;
400 isst_cpu_info[cpu].numa_node = cpu_to_node(cpu);
338 401
339 isst_restore_msr_local(cpu);402 isst_restore_msr_local(cpu);
340 403
drivers/platform/x86/samsung-laptop.c+14−21
@@ -388,7 +388,7 @@ MODULE_PARM_DESC(force, · 387 unmodified lines
388 "Disable the DMI check and forces the driver to be loaded");388 "Disable the DMI check and forces the driver to be loaded");
389 389
390static bool debug;390static bool debug;
391module_param(debug, bool, S_IRUGO | S_IWUSR);391module_param(debug, bool, 0644);
392MODULE_PARM_DESC(debug, "Debug enabled or not");392MODULE_PARM_DESC(debug, "Debug enabled or not");
393 393
394static int sabi_command(struct samsung_laptop *samsung, u16 command,394static 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
705 return count;705 return count;
706}706}
707 707
708static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,708static DEVICE_ATTR(performance_level, 0644,
709 get_performance_level, set_performance_level);709 get_performance_level, set_performance_level);
710 710
711static int read_battery_life_extender(struct samsung_laptop *samsung)711static 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
774 return count;774 return count;
775}775}
776 776
777static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,777static DEVICE_ATTR(battery_life_extender, 0644,
778 get_battery_life_extender, set_battery_life_extender);778 get_battery_life_extender, set_battery_life_extender);
779 779
780static int read_usb_charge(struct samsung_laptop *samsung)780static int read_usb_charge(struct samsung_laptop *samsung)
@@ -843,7 +843,7 @@ static ssize_t set_usb_charge(struct device *dev, · 62 unmodified lines
843 return count;843 return count;
844}844}
845 845
846static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,846static DEVICE_ATTR(usb_charge, 0644,
847 get_usb_charge, set_usb_charge);847 get_usb_charge, set_usb_charge);
848 848
849static int read_lid_handling(struct samsung_laptop *samsung)849static int read_lid_handling(struct samsung_laptop *samsung)
@@ -908,7 +908,7 @@ static ssize_t set_lid_handling(struct device *dev, · 58 unmodified lines
908 return count;908 return count;
909}909}
910 910
911static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO,911static DEVICE_ATTR(lid_handling, 0644,
912 get_lid_handling, set_lid_handling);912 get_lid_handling, set_lid_handling);
913 913
914static struct attribute *platform_attributes[] = {914static struct attribute *platform_attributes[] = {
@@ -1291,24 +1291,17 @@ static void samsung_debugfs_init(struct samsung_laptop *samsung) · 376 unmodified lines
1291 samsung->debug.sdiag_wrapper.data = samsung->sdiag;1291 samsung->debug.sdiag_wrapper.data = samsung->sdiag;
1292 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);1292 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
1293 1293
1294 debugfs_create_u16("command", S_IRUGO | S_IWUSR, root,1294 debugfs_create_u16("command", 0644, root, &samsung->debug.command);
1295 &samsung->debug.command);1295 debugfs_create_u32("d0", 0644, root, &samsung->debug.data.d0);
1296 debugfs_create_u32("d0", S_IRUGO | S_IWUSR, root,1296 debugfs_create_u32("d1", 0644, root, &samsung->debug.data.d1);
1297 &samsung->debug.data.d0);1297 debugfs_create_u16("d2", 0644, root, &samsung->debug.data.d2);
1298 debugfs_create_u32("d1", S_IRUGO | S_IWUSR, root,1298 debugfs_create_u8("d3", 0644, root, &samsung->debug.data.d3);
1299 &samsung->debug.data.d1);1299 debugfs_create_blob("data", 0444, root, &samsung->debug.data_wrapper);
1300 debugfs_create_u16("d2", S_IRUGO | S_IWUSR, root,1300 debugfs_create_blob("f0000_segment", 0400, 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,
1307 &samsung->debug.f0000_wrapper);1301 &samsung->debug.f0000_wrapper);
1308 debugfs_create_file("call", S_IFREG | S_IRUGO, root, samsung,1302 debugfs_create_file("call", 0444, root, samsung,
1309 &samsung_laptop_call_fops);1303 &samsung_laptop_call_fops);
1310 debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, root,1304 debugfs_create_blob("sdiag", 0444, root, &samsung->debug.sdiag_wrapper);
1311 &samsung->debug.sdiag_wrapper);
1312}1305}
1313 1306
1314static void samsung_sabi_exit(struct samsung_laptop *samsung)1307static 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
156 NULL156 NULL
157};157};
158 158
159static struct attribute_group tc1100_attribute_group = {159static const struct attribute_group tc1100_attribute_group = {
160 .attrs = tc1100_attributes,160 .attrs = tc1100_attributes,
161};161};
162 162
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
124static 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
132static const char * const encoding_options[] = {
133 [TLMI_ENCODING_ASCII] = "ascii",
134 [TLMI_ENCODING_SCANCODE] = "scancode",
135};
136static struct think_lmi tlmi_priv;
137static struct class *fw_attr_class;
138
139/* ------ Utility functions ------------*/
140/* Convert BIOS WMI error string to suitable error code */
141static 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 */
153static 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 */
167static 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 */
197static 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 */
219static 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
255static int tlmi_save_bios_settings(const char *password)
256{
257 return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID,
258 password);
259}
260
261static 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
278static 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 --------------------------------------------------------- */
299static 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
307static struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled);
308
309static 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
329static struct kobj_attribute auth_current_password = __ATTR_WO(current_password);
330
331static 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);
371out:
372 kfree(new_pwd);
373 return ret ?: count;
374}
375
376static struct kobj_attribute auth_new_password = __ATTR_WO(new_password);
377
378static 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
386static struct kobj_attribute auth_min_pass_length = __ATTR_RO(min_password_length);
387
388static 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}
395static struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_length);
396
397static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr,
398 char *buf)
399{
400 return sysfs_emit(buf, "password\n");
401}
402static struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism);
403
404static 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
412static 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
428static struct kobj_attribute auth_encoding = __ATTR_RW(encoding);
429
430static 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
438static 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
455static struct kobj_attribute auth_kbdlang = __ATTR_RW(kbdlang);
456
457static 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}
464static struct kobj_attribute auth_role = __ATTR_RO(role);
465
466static 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
479static const struct attribute_group auth_attr_group = {
480 .attrs = auth_attrs,
481};
482
483/* ---- Attributes sysfs --------------------------------------------------------- */
484static 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
492static 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
512static 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
522static 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
574out:
575 kfree(auth_str);
576 kfree(set_str);
577 kfree(new_setting);
578 return ret ?: count;
579}
580
581static struct kobj_attribute attr_displ_name = __ATTR_RO(display_name);
582
583static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values);
584
585static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600);
586
587static struct attribute *tlmi_attrs[] = {
588 &attr_displ_name.attr,
589 &attr_current_val.attr,
590 &attr_possible_values.attr,
591 NULL
592};
593
594static const struct attribute_group tlmi_attr_group = {
595 .attrs = tlmi_attrs,
596};
597
598static 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
609static 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
620static const struct sysfs_ops tlmi_kobj_sysfs_ops = {
621 .show = tlmi_attr_show,
622 .store = tlmi_attr_store,
623};
624
625static 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
633static 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
640static struct kobj_type tlmi_attr_setting_ktype = {
641 .release = &tlmi_attr_setting_release,
642 .sysfs_ops = &tlmi_kobj_sysfs_ops,
643};
644
645static struct kobj_type tlmi_pwd_setting_ktype = {
646 .release = &tlmi_pwd_setting_release,
647 .sysfs_ops = &tlmi_kobj_sysfs_ops,
648};
649
650/* ---- Initialisation --------------------------------------------------------- */
651static 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
672static 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
751fail_create_attr:
752 tlmi_release_attr();
753fail_device_created:
754 device_destroy(fw_attr_class, MKDEV(0, 0));
755fail_class_created:
756 fw_attributes_class_put();
757 return ret;
758}
759
760/* ---- Base Driver -------------------------------------------------------- */
761static 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
864fail_clear_attr:
865 for (i = 0; i < TLMI_SETTINGS_COUNT; ++i)
866 kfree(tlmi_priv.setting[i]);
867 return ret;
868}
869
870static 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
877static int tlmi_probe(struct wmi_device *wdev, const void *context)
878{
879 tlmi_analyze();
880 return tlmi_sysfs_init();
881}
882
883static const struct wmi_device_id tlmi_id_table[] = {
884 { .guid_string = LENOVO_BIOS_SETTING_GUID },
885 { }
886};
887MODULE_DEVICE_TABLE(wmi, tlmi_id_table);
888
889static 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
898MODULE_AUTHOR("Sugumaran L <slacshiminar@lenovo.com>");
899MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
900MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
901MODULE_DESCRIPTION("ThinkLMI Driver");
902MODULE_LICENSE("GPL");
903
904module_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 */
14struct tlmi_err_codes {
15 const char *err_str;
16 int err_code;
17};
18
19enum encoding_option {
20 TLMI_ENCODING_ASCII,
21 TLMI_ENCODING_SCANCODE,
22};
23
24/* password configuration details */
25struct 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 */
35struct 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 */
48struct tlmi_attr_setting {
49 struct kobject kobj;
50 int index;
51 char display_name[TLMI_SETTINGS_MAXLEN];
52 char *possible_values;
53};
54
55struct 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
7938 continue;7938 continue;
7939 } else if (sscanf(cmd, "level %u", &l) == 1 &&7939 } else if (sscanf(cmd, "level %u", &l) == 1 &&
7940 l >= 0 && l <= TP_EC_VOLUME_MAX) {7940 l >= 0 && l <= TP_EC_VOLUME_MAX) {
7941 new_level = l;7941 new_level = l;
7942 continue;7942 continue;
7943 }7943 }
7944 }7944 }
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
2831 2831
2832 if (!dev->info_supported && !dev->system_event_supported) {2832 if (!dev->info_supported && !dev->system_event_supported) {
2833 pr_warn("No hotkey query interface found\n");2833 pr_warn("No hotkey query interface found\n");
2834 error = -EINVAL;
2834 goto err_remove_filter;2835 goto err_remove_filter;
2835 }2836 }
2836 2837
drivers/platform/x86/toshiba_haps.c+1−1
@@ -131,7 +131,7 @@ static const struct attribute_group haps_attr_group = { · 130 unmodified lines
131 */131 */
132static void toshiba_haps_notify(struct acpi_device *device, u32 event)132static void toshiba_haps_notify(struct acpi_device *device, u32 event)
133{133{
134 pr_debug("Received event: 0x%x", event);134 pr_debug("Received event: 0x%x\n", event);
135 135
136 acpi_bus_generate_netlink_event(device->pnp.device_class,136 acpi_bus_generate_netlink_event(device->pnp.device_class,
137 dev_name(&device->dev),137 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
299 .properties = estar_beauty_hd_props,299 .properties = estar_beauty_hd_props,
300};300};
301 301
302/* Generic props + data for upside-down mounted GDIX1001 touchscreens */
303static 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
309static const struct ts_dmi_data gdix1001_00_upside_down_data = {
310 .acpi_name = "GDIX1001:00",
311 .properties = gdix1001_upside_down_props,
312};
313
314static const struct ts_dmi_data gdix1001_01_upside_down_data = {
315 .acpi_name = "GDIX1001:01",
316 .properties = gdix1001_upside_down_props,
317};
318
319static 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
326static const struct ts_dmi_data glavey_tm800a550l_data = {
327 .acpi_name = "GDIX1001:00",
328 .properties = glavey_tm800a550l_props,
329};
330
302static const struct property_entry gp_electronic_t701_props[] = {331static const struct property_entry gp_electronic_t701_props[] = {
303 PROPERTY_ENTRY_U32("touchscreen-size-x", 960),332 PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
304 PROPERTY_ENTRY_U32("touchscreen-size-y", 640),333 PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
@@ -942,7 +971,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 637 unmodified lines
942 },971 },
943 },972 },
944 {973 {
945 /* Chuwi Hi10 Prus (CWI597) */974 /* Chuwi Hi10 Pro (CWI529) */
946 .driver_data = (void *)&chuwi_hi10_pro_data,975 .driver_data = (void *)&chuwi_hi10_pro_data,
947 .matches = {976 .matches = {
948 DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),977 DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
@@ -1038,6 +1067,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 89 unmodified lines
1038 DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"),1067 DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"),
1039 },1068 },
1040 },1069 },
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 },
1041 {1079 {
1042 /* GP-electronic T701 */1080 /* GP-electronic T701 */
1043 .driver_data = (void *)&gp_electronic_t701_data,1081 .driver_data = (void *)&gp_electronic_t701_data,
@@ -1330,6 +1368,24 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 286 unmodified lines
1330 DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"),1368 DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"),
1331 },1369 },
1332 },1370 },
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 },
1333 {1389 {
1334 /* Teclast X98 Plus II */1390 /* Teclast X98 Plus II */
1335 .driver_data = (void *)&teclast_x98plus2_data,1391 .driver_data = (void *)&teclast_x98plus2_data,
@@ -1338,6 +1394,19 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 2 unmodified lines
1338 DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"),1394 DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"),
1339 },1395 },
1340 },1396 },
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 },
1341 {1410 {
1342 /* Trekstor Primebook C11 */1411 /* Trekstor Primebook C11 */
1343 .driver_data = (void *)&trekstor_primebook_c11_data,1412 .driver_data = (void *)&trekstor_primebook_c11_data,
@@ -1413,6 +1482,22 @@ const struct dmi_system_id touchscreen_dmi_table[] = { · 69 unmodified lines
1413 DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"),1482 DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"),
1414 },1483 },
1415 },1484 },
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 },
1416 {1501 {
1417 /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */1502 /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
1418 .driver_data = (void *)&chuwi_vi8_data,1503 .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
778 NULL,778 NULL,
779};779};
780 780
781static struct attribute_group base_attr_group = {781static const struct attribute_group base_attr_group = {
782 .attrs = base_attrs782 .attrs = base_attrs
783};783};
784 784
@@ -823,7 +823,7 @@ static struct attribute *hubless_base_attrs[] = { · 38 unmodified lines
823 NULL,823 NULL,
824};824};
825 825
826static struct attribute_group hubless_base_attr_group = {826static const struct attribute_group hubless_base_attr_group = {
827 .attrs = hubless_base_attrs827 .attrs = hubless_base_attrs
828};828};
829 829
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
17MODULE_LICENSE("GPL");
18MODULE_AUTHOR("Alex Hung");
19MODULE_ALIAS("acpi*:HPQ6001:*");
20MODULE_ALIAS("acpi*:WSTADEF:*");
21MODULE_ALIAS("acpi*:AMDI0051:*");
22
23static struct input_dev *wl_input_dev;
24
25static const struct acpi_device_id wl_ids[] = {
26 {"HPQ6001", 0},
27 {"WSTADEF", 0},
28 {"AMDI0051", 0},
29 {"", 0},
30};
31
32static 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
52err_free_dev:
53 input_free_device(wl_input_dev);
54 return err;
55}
56
57static void wireless_input_destroy(void)
58{
59 input_unregister_device(wl_input_dev);
60}
61
62static 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
75static 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
86static int wl_remove(struct acpi_device *device)
87{
88 wireless_input_destroy();
89 return 0;
90}
91
92static 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
103module_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
1097#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)1097#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
1098bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,1098bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
1099 struct acpi_resource_gpio **agpio);1099 struct acpi_resource_gpio **agpio);
1100bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
1101 struct acpi_resource_gpio **agpio);
1100int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index);1102int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index);
1101#else1103#else
1102static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,1104static 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
1104{1106{
1105 return false;1107 return false;
1106}1108}
1109static inline bool acpi_gpio_get_io_resource(struct acpi_resource *ares,
1110 struct acpi_resource_gpio **agpio)
1111{
1112 return false;
1113}
1107static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev,1114static inline int acpi_dev_gpio_irq_get_by(struct acpi_device *adev,
1108 const char *name, int index)1115 const char *name, int index)
1109{1116{
include/linux/devm-helpers.h+25−0
@@ -51,4 +51,29 @@ static inline int devm_delayed_work_autocancel(struct device *dev, · 50 unmodified lines
51 return devm_add_action(dev, devm_delayed_work_drop, w);51 return devm_add_action(dev, devm_delayed_work_drop, w);
52}52}
53 53
54static 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 */
71static 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
54#endif79#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
692 const struct acpi_gpio_mapping *gpios);692 const struct acpi_gpio_mapping *gpios);
693void devm_acpi_dev_remove_driver_gpios(struct device *dev);693void devm_acpi_dev_remove_driver_gpios(struct device *dev);
694 694
695struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label);
696
695#else /* CONFIG_GPIOLIB && CONFIG_ACPI */697#else /* CONFIG_GPIOLIB && CONFIG_ACPI */
696 698
697struct acpi_device;699struct acpi_device;
include/linux/surface_aggregator/controller.h+26−1
@@ -6,7 +6,7 @@ · 5 unmodified lines
6 * managing access and communication to and from the SSAM EC, as well as main6 * managing access and communication to and from the SSAM EC, as well as main
7 * communication structures and definitions.7 * communication structures and definitions.
8 *8 *
9 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>9 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
10 */10 */
11 11
12#ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H12#ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H
@@ -795,6 +795,20 @@ enum ssam_event_mask { · 782 unmodified lines
795#define SSAM_EVENT_REGISTRY_REG \795#define SSAM_EVENT_REGISTRY_REG \
796 SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02)796 SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02)
797 797
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 */
808enum ssam_event_notifier_flags {
809 SSAM_EVENT_NOTIFIER_OBSERVER = BIT(0),
810};
811
798/**812/**
799 * struct ssam_event_notifier - Notifier block for SSAM events.813 * struct ssam_event_notifier - Notifier block for SSAM events.
800 * @base: The base notifier block with callback function and priority.814 * @base: The base notifier block with callback function and priority.
@@ -803,6 +817,7 @@ enum ssam_event_mask { · 2 unmodified lines
803 * @event.id: ID specifying the event.817 * @event.id: ID specifying the event.
804 * @event.mask: Flags determining how events are matched to the notifier.818 * @event.mask: Flags determining how events are matched to the notifier.
805 * @event.flags: Flags used for enabling the event.819 * @event.flags: Flags used for enabling the event.
820 * @flags: Notifier flags (see &enum ssam_event_notifier_flags).
806 */821 */
807struct ssam_event_notifier {822struct ssam_event_notifier {
808 struct ssam_notifier_block base;823 struct ssam_notifier_block base;
@@ -813,6 +828,8 @@ struct ssam_event_notifier { · 4 unmodified lines
813 enum ssam_event_mask mask;828 enum ssam_event_mask mask;
814 u8 flags;829 u8 flags;
815 } event;830 } event;
831
832 unsigned long flags;
816};833};
817 834
818int ssam_notifier_register(struct ssam_controller *ctrl,835int ssam_notifier_register(struct ssam_controller *ctrl,
@@ -821,4 +838,12 @@ int ssam_notifier_register(struct ssam_controller *ctrl, · 2 unmodified lines
821int ssam_notifier_unregister(struct ssam_controller *ctrl,838int ssam_notifier_unregister(struct ssam_controller *ctrl,
822 struct ssam_event_notifier *n);839 struct ssam_event_notifier *n);
823 840
841int ssam_controller_event_enable(struct ssam_controller *ctrl,
842 struct ssam_event_registry reg,
843 struct ssam_event_id id, u8 flags);
844
845int ssam_controller_event_disable(struct ssam_controller *ctrl,
846 struct ssam_event_registry reg,
847 struct ssam_event_id id, u8 flags);
848
824#endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */849#endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */
include/linux/surface_aggregator/device.h+1−1
@@ -7,7 +7,7 @@ · 6 unmodified lines
7 * Provides support for non-platform/non-ACPI SSAM clients via dedicated7 * Provides support for non-platform/non-ACPI SSAM clients via dedicated
8 * subsystem.8 * subsystem.
9 *9 *
10 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>10 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
11 */11 */
12 12
13#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H13#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H
include/linux/surface_aggregator/serial_hub.h+1−1
@@ -6,7 +6,7 @@ · 5 unmodified lines
6 * Surface System Aggregator Module (SSAM). Provides the interface for basic6 * Surface System Aggregator Module (SSAM). Provides the interface for basic
7 * packet- and request-based communication with the SSAM EC via SSH.7 * packet- and request-based communication with the SSAM EC via SSH.
8 *8 *
9 * Copyright (C) 2019-2020 Maximilian Luz <luzmaximilian@gmail.com>9 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
10 */10 */
11 11
12#ifndef _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H12#ifndef _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H
include/uapi/linux/surface_aggregator/cdev.h+71−2
@@ -6,7 +6,7 @@ · 5 unmodified lines
6 * device. This device provides direct user-space access to the SSAM EC.6 * device. This device provides direct user-space access to the SSAM EC.
7 * Intended for debugging and development.7 * Intended for debugging and development.
8 *8 *
9 * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>9 * Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
10 */10 */
11 11
12#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H12#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H
@@ -73,6 +73,75 @@ struct ssam_cdev_request { · 60 unmodified lines
73 } response;73 } response;
74} __attribute__((__packed__));74} __attribute__((__packed__));
75 75
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 */
88struct 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 */
107struct 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 */
132struct 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)
77 146
78#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */147#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
15 int arg;15 int arg;
16};16};
17 17
18static const char *version_str = "v1.9";18static const char *version_str = "v1.10";
19static const int supported_api_ver = 1;19static const int supported_api_ver = 1;
20static struct isst_if_platform_info isst_platform_info;20static struct isst_if_platform_info isst_platform_info;
21static char *progname;21static char *progname;
@@ -106,6 +106,22 @@ int is_skx_based_platform(void) · 84 unmodified lines
106 return 0;106 return 0;
107}107}
108 108
109int is_spr_platform(void)
110{
111 if (cpu_model == 0x8F)
112 return 1;
113
114 return 0;
115}
116
117int is_icx_platform(void)
118{
119 if (cpu_model == 0x6A || cpu_model == 0x6C)
120 return 1;
121
122 return 0;
123}
124
109static int update_cpu_model(void)125static int update_cpu_model(void)
110{126{
111 unsigned int ebx, ecx, edx;127 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
201{201{
202 unsigned int resp;202 unsigned int resp;
203 int ret;203 int ret;
204
204 ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ,205 ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ,
205 0, config_index, &resp);206 0, config_index, &resp);
206 if (ret) {207 if (ret) {
@@ -209,6 +210,20 @@ void isst_get_uncore_mem_freq(int cpu, int config_index, · 2 unmodified lines
209 }210 }
210 211
211 ctdp_level->mem_freq = resp & GENMASK(7, 0);212 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 }
212 debug_printf(227 debug_printf(
213 "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n",228 "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n",
214 cpu, config_index, resp, ctdp_level->mem_freq);229 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
446 if (ctdp_level->mem_freq) {446 if (ctdp_level->mem_freq) {
447 snprintf(header, sizeof(header), "mem-frequency(MHz)");447 snprintf(header, sizeof(header), "mem-frequency(MHz)");
448 snprintf(value, sizeof(value), "%d",448 snprintf(value, sizeof(value), "%d",
449 ctdp_level->mem_freq * DISP_FREQ_MULTIPLIER);449 ctdp_level->mem_freq);
450 format_and_print(outf, level + 2, header, value);450 format_and_print(outf, level + 2, header, value);
451 }451 }
452 452
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
257extern int isst_read_pm_config(int cpu, int *cp_state, int *cp_cap);257extern int isst_read_pm_config(int cpu, int *cp_state, int *cp_cap);
258extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg);258extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg);
259extern int is_skx_based_platform(void);259extern int is_skx_based_platform(void);
260extern int is_spr_platform(void);
261extern int is_icx_platform(void);
260extern void isst_trl_display_information(int cpu, FILE *outf, unsigned long long trl);262extern void isst_trl_display_information(int cpu, FILE *outf, unsigned long long trl);
261#endif263#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