Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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
...

+4286 -693
+17 -1
Documentation/ABI/testing/sysfs-class-firmware-attributes
··· 197 197 Drivers may emit a CHANGE uevent when a password is set or unset 198 198 userspace may check it again. 199 199 200 - On Dell systems, if Admin password is set, then all BIOS attributes 200 + On Dell and Lenovo systems, if Admin password is set, then all BIOS attributes 201 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 203 219 What: /sys/class/firmware-attributes/*/attributes/pending_reboot 204 220 Date: February 2021
+55
Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi
··· 1 + What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type 2 + Date: Apr 2021 3 + KernelVersion: 5.13 4 + Contact: "perry.yuan@dell.com>" 5 + Description: 6 + Display which dell hardware level privacy devices are supported 7 + “Dell Privacy” is a set of HW, FW, and SW features to enhance 8 + Dell’s commitment to platform privacy for MIC, Camera, and 9 + ePrivacy screens. 10 + The supported hardware privacy devices are: 11 + Attributes: 12 + Microphone Mute: 13 + Identifies the local microphone can be muted by hardware, no applications 14 + is available to capture system mic sound 15 + 16 + Camera Shutter: 17 + Identifies camera shutter controlled by hardware, which is a micromechanical 18 + shutter assembly that is built onto the camera module to block capturing images 19 + from outside the laptop 20 + 21 + supported: 22 + The privacy device is supported by this system 23 + 24 + unsupported: 25 + The privacy device is not supported on this system 26 + 27 + For example to check which privacy devices are supported: 28 + 29 + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type 30 + [Microphone Mute] [supported] 31 + [Camera Shutter] [supported] 32 + [ePrivacy Screen] [unsupported] 33 + 34 + What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state 35 + Date: Apr 2021 36 + KernelVersion: 5.13 37 + Contact: "perry.yuan@dell.com>" 38 + Description: 39 + Allow user space to check current dell privacy device state. 40 + Describes the Device State class exposed by BIOS which can be 41 + consumed by various applications interested in knowing the Privacy 42 + feature capabilities 43 + Attributes: 44 + muted: 45 + Identifies the privacy device is turned off and cannot send stream to OS applications 46 + 47 + unmuted: 48 + Identifies the privacy device is turned on ,audio or camera driver can get 49 + stream from mic and camera module to OS applications 50 + 51 + For example to check all supported current privacy device states: 52 + 53 + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state 54 + [Microphone] [unmuted] 55 + [Camera Shutter] [unmuted]
+122 -5
Documentation/driver-api/surface_aggregator/clients/cdev.rst
··· 1 1 .. SPDX-License-Identifier: GPL-2.0+ 2 2 3 - .. |u8| replace:: :c:type:`u8 <u8>` 4 - .. |u16| replace:: :c:type:`u16 <u16>` 5 3 .. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>` 6 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 ============================== 9 8 User-Space EC Interface (cdev) ··· 21 22 22 23 A small python library and scripts for accessing this interface can be found 23 24 at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam. 25 + 26 + .. contents:: 27 + 28 + 29 + Receiving Events 30 + ================ 31 + 32 + Events can be received by reading from the device-file. The are represented by 33 + the |ssam_cdev_event| datatype. 34 + 35 + Before events are available to be read, however, the desired notifiers must be 36 + registered via the ``SSAM_CDEV_NOTIF_REGISTER`` IOCTL. Notifiers are, in 37 + essence, callbacks, called when the EC sends an event. They are, in this 38 + interface, associated with a specific target category and device-file-instance. 39 + They forward any event of this category to the buffer of the corresponding 40 + instance, from which it can then be read. 41 + 42 + Notifiers themselves do not enable events on the EC. Thus, it may additionally 43 + be necessary to enable events via the ``SSAM_CDEV_EVENT_ENABLE`` IOCTL. While 44 + notifiers work per-client (i.e. per-device-file-instance), events are enabled 45 + globally, for the EC and all of its clients (regardless of userspace or 46 + non-userspace). The ``SSAM_CDEV_EVENT_ENABLE`` and ``SSAM_CDEV_EVENT_DISABLE`` 47 + IOCTLs take care of reference counting the events, such that an event is 48 + enabled as long as there is a client that has requested it. 49 + 50 + Note that enabled events are not automatically disabled once the client 51 + instance is closed. Therefore any client process (or group of processes) should 52 + balance their event enable calls with the corresponding event disable calls. It 53 + is, however, perfectly valid to enable and disable events on different client 54 + instances. For example, it is valid to set up notifiers and read events on 55 + client instance ``A``, enable those events on instance ``B`` (note that these 56 + will also be received by A since events are enabled/disabled globally), and 57 + after no more events are desired, disable the previously enabled events via 58 + instance ``C``. 24 59 25 60 26 61 Controller IOCTLs ··· 78 45 - ``REQUEST`` 79 46 - Perform synchronous SAM request. 80 47 48 + * - ``0xA5`` 49 + - ``2`` 50 + - ``W`` 51 + - ``NOTIF_REGISTER`` 52 + - Register event notifier. 81 53 82 - ``REQUEST`` 83 - ----------- 54 + * - ``0xA5`` 55 + - ``3`` 56 + - ``W`` 57 + - ``NOTIF_UNREGISTER`` 58 + - Unregister event notifier. 59 + 60 + * - ``0xA5`` 61 + - ``4`` 62 + - ``W`` 63 + - ``EVENT_ENABLE`` 64 + - Enable event source. 65 + 66 + * - ``0xA5`` 67 + - ``5`` 68 + - ``W`` 69 + - ``EVENT_DISABLE`` 70 + - Disable event source. 71 + 72 + 73 + ``SSAM_CDEV_REQUEST`` 74 + --------------------- 84 75 85 76 Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``. 86 77 ··· 139 82 inside the IOCTL, but the request ``status`` member may still be negative in 140 83 case the actual execution of the request failed after it has been submitted. 141 84 142 - A full definition of the argument struct is provided below: 85 + A full definition of the argument struct is provided below. 86 + 87 + ``SSAM_CDEV_NOTIF_REGISTER`` 88 + ---------------------------- 89 + 90 + Defined as ``_IOW(0xA5, 2, struct ssam_cdev_notifier_desc)``. 91 + 92 + Register a notifier for the event target category specified in the given 93 + notifier description with the specified priority. Notifiers registration is 94 + required to receive events, but does not enable events themselves. After a 95 + notifier for a specific target category has been registered, all events of that 96 + category will be forwarded to the userspace client and can then be read from 97 + the device file instance. Note that events may have to be enabled, e.g. via the 98 + ``SSAM_CDEV_EVENT_ENABLE`` IOCTL, before the EC will send them. 99 + 100 + Only one notifier can be registered per target category and client instance. If 101 + a notifier has already been registered, this IOCTL will fail with ``-EEXIST``. 102 + 103 + Notifiers will automatically be removed when the device file instance is 104 + closed. 105 + 106 + ``SSAM_CDEV_NOTIF_UNREGISTER`` 107 + ------------------------------ 108 + 109 + Defined as ``_IOW(0xA5, 3, struct ssam_cdev_notifier_desc)``. 110 + 111 + Unregisters the notifier associated with the specified target category. The 112 + priority field will be ignored by this IOCTL. If no notifier has been 113 + registered for this client instance and the given category, this IOCTL will 114 + fail with ``-ENOENT``. 115 + 116 + ``SSAM_CDEV_EVENT_ENABLE`` 117 + -------------------------- 118 + 119 + Defined as ``_IOW(0xA5, 4, struct ssam_cdev_event_desc)``. 120 + 121 + Enable the event associated with the given event descriptor. 122 + 123 + Note that this call will not register a notifier itself, it will only enable 124 + events on the controller. If you want to receive events by reading from the 125 + device file, you will need to register the corresponding notifier(s) on that 126 + instance. 127 + 128 + Events are not automatically disabled when the device file is closed. This must 129 + be done manually, via a call to the ``SSAM_CDEV_EVENT_DISABLE`` IOCTL. 130 + 131 + ``SSAM_CDEV_EVENT_DISABLE`` 132 + --------------------------- 133 + 134 + Defined as ``_IOW(0xA5, 5, struct ssam_cdev_event_desc)``. 135 + 136 + Disable the event associated with the given event descriptor. 137 + 138 + Note that this will not unregister any notifiers. Events may still be received 139 + and forwarded to user-space after this call. The only safe way of stopping 140 + events from being received is unregistering all previously registered 141 + notifiers. 142 + 143 + 144 + Structures and Enums 145 + ==================== 143 146 144 147 .. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
+1 -1
Documentation/userspace-api/ioctl/ioctl-number.rst
··· 325 325 0xA3 90-9F linux/dtlk.h 326 326 0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem 327 327 0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org> 328 - 0xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator 328 + 0xA5 01-05 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator 329 329 <mailto:luzmaximilian@gmail.com> 330 330 0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver 331 331 <mailto:luzmaximilian@gmail.com>
+22 -3
MAINTAINERS
··· 5187 5187 M: Matthew Garrett <mjg59@srcf.ucam.org> 5188 5188 M: Pali Rohár <pali@kernel.org> 5189 5189 S: Maintained 5190 - F: drivers/platform/x86/dell/dell-wmi.c 5190 + F: drivers/platform/x86/dell/dell-wmi-base.c 5191 + 5192 + DELL WMI HARDWARE PRIVACY SUPPORT 5193 + M: Perry Yuan <Perry.Yuan@dell.com> 5194 + L: Dell.Client.Kernel@dell.com 5195 + L: platform-driver-x86@vger.kernel.org 5196 + S: Maintained 5197 + F: drivers/platform/x86/dell/dell-wmi-privacy.c 5191 5198 5192 5199 DELTA ST MEDIA DRIVER 5193 5200 M: Hugues Fruchet <hugues.fruchet@foss.st.com> ··· 9404 9397 F: arch/x86/include/asm/intel_scu_ipc.h 9405 9398 F: drivers/platform/x86/intel_scu_* 9406 9399 9400 + INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER 9401 + M: Daniel Scally <djrscally@gmail.com> 9402 + S: Maintained 9403 + F: drivers/platform/x86/intel/int3472/ 9404 + 9407 9405 INTEL SPEED SELECT TECHNOLOGY 9408 9406 M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 9409 9407 L: platform-driver-x86@vger.kernel.org ··· 9429 9417 F: include/linux/firmware/intel/stratix10-svc-client.h 9430 9418 9431 9419 INTEL TELEMETRY DRIVER 9432 - M: Rajneesh Bhardwaj <rajneesh.bhardwaj@linux.intel.com> 9420 + M: Rajneesh Bhardwaj <irenic.rajneesh@gmail.com> 9433 9421 M: "David E. Box" <david.e.box@linux.intel.com> 9434 9422 L: platform-driver-x86@vger.kernel.org 9435 9423 S: Maintained ··· 12221 12209 L: platform-driver-x86@vger.kernel.org 12222 12210 S: Maintained 12223 12211 W: https://github.com/linux-surface/surface-aggregator-module 12224 - C: irc://chat.freenode.net/##linux-surface 12212 + C: irc://irc.libera.chat/linux-surface 12225 12213 F: Documentation/driver-api/surface_aggregator/ 12226 12214 F: drivers/platform/surface/aggregator/ 12227 12215 F: drivers/platform/surface/surface_acpi_notify.c ··· 18226 18214 W: http://thinkwiki.org/wiki/Ibm-acpi 18227 18215 T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git 18228 18216 F: drivers/platform/x86/thinkpad_acpi.c 18217 + 18218 + THINKPAD LMI DRIVER 18219 + M: Mark Pearson <markpearson@lenovo.com> 18220 + L: platform-driver-x86@vger.kernel.org 18221 + S: Maintained 18222 + F: Documentation/ABI/testing/sysfs-class-firmware-attributes 18223 + F: drivers/platform/x86/think-lmi.? 18229 18224 18230 18225 THUNDERBOLT DMA TRAFFIC TEST DRIVER 18231 18226 M: Isaac Hazan <isaac.hazan@intel.com>
+1 -1
drivers/acpi/pmic/Kconfig
··· 52 52 53 53 config TPS68470_PMIC_OPREGION 54 54 bool "ACPI operation region support for TPS68470 PMIC" 55 - depends on MFD_TPS68470 55 + depends on INTEL_SKL_INT3472 56 56 help 57 57 This config adds ACPI operation region support for TI TPS68470 PMIC. 58 58 TPS68470 device is an advanced power management unit that powers
+5 -11
drivers/extcon/extcon-max14577.c
··· 6 6 // Chanwoo Choi <cw00.choi@samsung.com> 7 7 // Krzysztof Kozlowski <krzk@kernel.org> 8 8 9 + #include <linux/devm-helpers.h> 9 10 #include <linux/kernel.h> 10 11 #include <linux/module.h> 11 12 #include <linux/i2c.h> ··· 674 673 platform_set_drvdata(pdev, info); 675 674 mutex_init(&info->mutex); 676 675 677 - INIT_WORK(&info->irq_work, max14577_muic_irq_work); 676 + ret = devm_work_autocancel(&pdev->dev, &info->irq_work, 677 + max14577_muic_irq_work); 678 + if (ret) 679 + return ret; 678 680 679 681 switch (max14577->dev_type) { 680 682 case MAXIM_DEVICE_TYPE_MAX77836: ··· 770 766 return ret; 771 767 } 772 768 773 - static int max14577_muic_remove(struct platform_device *pdev) 774 - { 775 - struct max14577_muic_info *info = platform_get_drvdata(pdev); 776 - 777 - cancel_work_sync(&info->irq_work); 778 - 779 - return 0; 780 - } 781 - 782 769 static const struct platform_device_id max14577_muic_id[] = { 783 770 { "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, }, 784 771 { "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, }, ··· 792 797 .of_match_table = of_max14577_muic_dt_match, 793 798 }, 794 799 .probe = max14577_muic_probe, 795 - .remove = max14577_muic_remove, 796 800 .id_table = max14577_muic_id, 797 801 }; 798 802
+5 -12
drivers/extcon/extcon-max77693.c
··· 5 5 // Copyright (C) 2012 Samsung Electrnoics 6 6 // Chanwoo Choi <cw00.choi@samsung.com> 7 7 8 + #include <linux/devm-helpers.h> 8 9 #include <linux/kernel.h> 9 10 #include <linux/module.h> 10 11 #include <linux/i2c.h> ··· 1128 1127 platform_set_drvdata(pdev, info); 1129 1128 mutex_init(&info->mutex); 1130 1129 1131 - INIT_WORK(&info->irq_work, max77693_muic_irq_work); 1130 + ret = devm_work_autocancel(&pdev->dev, &info->irq_work, 1131 + max77693_muic_irq_work); 1132 + if (ret) 1133 + return ret; 1132 1134 1133 1135 /* Support irq domain for MAX77693 MUIC device */ 1134 1136 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { ··· 1258 1254 return ret; 1259 1255 } 1260 1256 1261 - static int max77693_muic_remove(struct platform_device *pdev) 1262 - { 1263 - struct max77693_muic_info *info = platform_get_drvdata(pdev); 1264 - 1265 - cancel_work_sync(&info->irq_work); 1266 - input_unregister_device(info->dock); 1267 - 1268 - return 0; 1269 - } 1270 - 1271 1257 static struct platform_driver max77693_muic_driver = { 1272 1258 .driver = { 1273 1259 .name = DEV_NAME, 1274 1260 }, 1275 1261 .probe = max77693_muic_probe, 1276 - .remove = max77693_muic_remove, 1277 1262 }; 1278 1263 1279 1264 module_platform_driver(max77693_muic_driver);
+15 -30
drivers/extcon/extcon-max8997.c
··· 5 5 // Copyright (C) 2012 Samsung Electronics 6 6 // Donggeun Kim <dg77.kim@samsung.com> 7 7 8 + #include <linux/devm-helpers.h> 8 9 #include <linux/kernel.h> 9 10 #include <linux/module.h> 10 11 #include <linux/i2c.h> ··· 651 650 mutex_init(&info->mutex); 652 651 653 652 INIT_WORK(&info->irq_work, max8997_muic_irq_work); 653 + ret = devm_work_autocancel(&pdev->dev, &info->irq_work, 654 + max8997_muic_irq_work); 655 + if (ret) 656 + return ret; 654 657 655 658 for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { 656 659 struct max8997_muic_irq *muic_irq = &muic_irqs[i]; 657 660 unsigned int virq = 0; 658 661 659 662 virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq); 660 - if (!virq) { 661 - ret = -EINVAL; 662 - goto err_irq; 663 - } 663 + if (!virq) 664 + return -EINVAL; 665 + 664 666 muic_irq->virq = virq; 665 667 666 - ret = request_threaded_irq(virq, NULL, 667 - max8997_muic_irq_handler, 668 - IRQF_NO_SUSPEND, 669 - muic_irq->name, info); 668 + ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, 669 + max8997_muic_irq_handler, 670 + IRQF_NO_SUSPEND, 671 + muic_irq->name, info); 670 672 if (ret) { 671 673 dev_err(&pdev->dev, 672 674 "failed: irq request (IRQ: %d, error :%d)\n", 673 675 muic_irq->irq, ret); 674 - goto err_irq; 676 + return ret; 675 677 } 676 678 } 677 679 ··· 682 678 info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable); 683 679 if (IS_ERR(info->edev)) { 684 680 dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); 685 - ret = PTR_ERR(info->edev); 686 - goto err_irq; 681 + return PTR_ERR(info->edev); 687 682 } 688 683 689 684 ret = devm_extcon_dev_register(&pdev->dev, info->edev); 690 685 if (ret) { 691 686 dev_err(&pdev->dev, "failed to register extcon device\n"); 692 - goto err_irq; 687 + return ret; 693 688 } 694 689 695 690 if (pdata && pdata->muic_pdata) { ··· 759 756 delay_jiffies); 760 757 761 758 return 0; 762 - 763 - err_irq: 764 - while (--i >= 0) 765 - free_irq(muic_irqs[i].virq, info); 766 - return ret; 767 - } 768 - 769 - static int max8997_muic_remove(struct platform_device *pdev) 770 - { 771 - struct max8997_muic_info *info = platform_get_drvdata(pdev); 772 - int i; 773 - 774 - for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) 775 - free_irq(muic_irqs[i].virq, info); 776 - cancel_work_sync(&info->irq_work); 777 - 778 - return 0; 779 759 } 780 760 781 761 static struct platform_driver max8997_muic_driver = { ··· 766 780 .name = DEV_NAME, 767 781 }, 768 782 .probe = max8997_muic_probe, 769 - .remove = max8997_muic_remove, 770 783 }; 771 784 772 785 module_platform_driver(max8997_muic_driver);
+1 -1
drivers/gpio/Kconfig
··· 1367 1367 1368 1368 config GPIO_TPS68470 1369 1369 bool "TPS68470 GPIO" 1370 - depends on MFD_TPS68470 1370 + depends on INTEL_SKL_INT3472 1371 1371 help 1372 1372 Select this option to enable GPIO driver for the TPS68470 1373 1373 chip family.
+1 -9
drivers/gpio/gpio-crystalcove.c
··· 339 339 if (!cg) 340 340 return -ENOMEM; 341 341 342 - platform_set_drvdata(pdev, cg); 343 - 344 342 mutex_init(&cg->buslock); 345 343 cg->chip.label = KBUILD_MODNAME; 346 344 cg->chip.direction_input = crystalcove_gpio_dir_in; ··· 370 372 return retval; 371 373 } 372 374 373 - retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); 374 - if (retval) { 375 - dev_warn(&pdev->dev, "add gpio chip error: %d\n", retval); 376 - return retval; 377 - } 378 - 379 - return 0; 375 + return devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); 380 376 } 381 377 382 378 static struct platform_driver crystalcove_gpio_driver = {
+19 -20
drivers/gpio/gpio-wcove.c
··· 99 99 bool set_irq_mask; 100 100 }; 101 101 102 - static inline int to_reg(int gpio, enum ctrl_register reg_type) 102 + static inline int to_reg(int gpio, enum ctrl_register type) 103 103 { 104 - unsigned int reg; 104 + unsigned int reg = type == CTRL_IN ? GPIO_IN_CTRL_BASE : GPIO_OUT_CTRL_BASE; 105 105 106 106 if (gpio >= WCOVE_GPIO_NUM) 107 107 return -EOPNOTSUPP; 108 108 109 - if (reg_type == CTRL_IN) 110 - reg = GPIO_IN_CTRL_BASE + gpio; 111 - else 112 - reg = GPIO_OUT_CTRL_BASE + gpio; 113 - 114 - return reg; 109 + return reg + gpio; 115 110 } 116 111 117 112 static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask) ··· 124 129 return reg; 125 130 } 126 131 127 - static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) 132 + static void wcove_update_irq_mask(struct wcove_gpio *wg, irq_hw_number_t gpio) 128 133 { 129 134 unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask); 130 135 ··· 134 139 regmap_clear_bits(wg->regmap, reg, mask); 135 140 } 136 141 137 - static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) 142 + static void wcove_update_irq_ctrl(struct wcove_gpio *wg, irq_hw_number_t gpio) 138 143 { 139 144 int reg = to_reg(gpio, CTRL_IN); 140 - 141 - if (reg < 0) 142 - return; 143 145 144 146 regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt); 145 147 } ··· 240 248 { 241 249 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 242 250 struct wcove_gpio *wg = gpiochip_get_data(chip); 251 + irq_hw_number_t gpio = irqd_to_hwirq(data); 243 252 244 - if (data->hwirq >= WCOVE_GPIO_NUM) 253 + if (gpio >= WCOVE_GPIO_NUM) 245 254 return 0; 246 255 247 256 switch (type) { ··· 279 286 { 280 287 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 281 288 struct wcove_gpio *wg = gpiochip_get_data(chip); 282 - int gpio = data->hwirq; 289 + irq_hw_number_t gpio = irqd_to_hwirq(data); 283 290 284 291 if (wg->update & UPDATE_IRQ_TYPE) 285 292 wcove_update_irq_ctrl(wg, gpio); ··· 294 301 { 295 302 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 296 303 struct wcove_gpio *wg = gpiochip_get_data(chip); 304 + irq_hw_number_t gpio = irqd_to_hwirq(data); 297 305 298 - if (data->hwirq >= WCOVE_GPIO_NUM) 306 + if (gpio >= WCOVE_GPIO_NUM) 299 307 return; 300 308 301 309 wg->set_irq_mask = false; ··· 307 313 { 308 314 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 309 315 struct wcove_gpio *wg = gpiochip_get_data(chip); 316 + irq_hw_number_t gpio = irqd_to_hwirq(data); 310 317 311 - if (data->hwirq >= WCOVE_GPIO_NUM) 318 + if (gpio >= WCOVE_GPIO_NUM) 312 319 return; 313 320 314 321 wg->set_irq_mask = true; ··· 364 369 return IRQ_HANDLED; 365 370 } 366 371 367 - static void wcove_gpio_dbg_show(struct seq_file *s, 368 - struct gpio_chip *chip) 372 + static void wcove_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 369 373 { 370 374 unsigned int ctlo, ctli, irq_mask, irq_status; 371 375 struct wcove_gpio *wg = gpiochip_get_data(chip); ··· 373 379 for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) { 374 380 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo); 375 381 ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli); 382 + if (ret) { 383 + dev_err(wg->dev, "Failed to read registers: CTRL out/in\n"); 384 + break; 385 + } 386 + 376 387 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask); 377 388 ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status); 378 389 if (ret) { 379 - pr_err("Failed to read registers: ctrl out/in or irq status/mask\n"); 390 + dev_err(wg->dev, "Failed to read registers: IRQ status/mask\n"); 380 391 break; 381 392 } 382 393
+51
drivers/gpio/gpiolib-acpi.c
··· 128 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 + */ 142 + struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label) 143 + { 144 + struct gpio_desc *gpio; 145 + int ret; 146 + 147 + gpio = acpi_get_gpiod(path, pin); 148 + if (IS_ERR(gpio)) 149 + return gpio; 150 + 151 + ret = gpiod_request(gpio, label); 152 + if (ret) 153 + return ERR_PTR(ret); 154 + 155 + return gpio; 156 + } 157 + EXPORT_SYMBOL_GPL(acpi_get_and_request_gpiod); 158 + 131 159 static irqreturn_t acpi_gpio_irq_handler(int irq, void *data) 132 160 { 133 161 struct acpi_gpio_event *event = data; ··· 195 167 return true; 196 168 } 197 169 EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); 170 + 171 + /** 172 + * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO 173 + * I/O resource or return False if not. 174 + * @ares: Pointer to the ACPI resource to fetch 175 + * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer 176 + */ 177 + bool acpi_gpio_get_io_resource(struct acpi_resource *ares, 178 + struct acpi_resource_gpio **agpio) 179 + { 180 + struct acpi_resource_gpio *gpio; 181 + 182 + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) 183 + return false; 184 + 185 + gpio = &ares->data.gpio; 186 + if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO) 187 + return false; 188 + 189 + *agpio = gpio; 190 + return true; 191 + } 192 + EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource); 198 193 199 194 static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, 200 195 struct acpi_gpio_event *event)
-52
drivers/input/touchscreen/goodix.c
··· 178 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 - */ 185 - static const struct dmi_system_id rotated_screen[] = { 186 - #if defined(CONFIG_DMI) && defined(CONFIG_X86) 187 - { 188 - .ident = "Teclast X89", 189 - .matches = { 190 - /* tPAD is too generic, also match on bios date */ 191 - DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), 192 - DMI_MATCH(DMI_BOARD_NAME, "tPAD"), 193 - DMI_MATCH(DMI_BIOS_DATE, "12/19/2014"), 194 - }, 195 - }, 196 - { 197 - .ident = "Teclast X98 Pro", 198 - .matches = { 199 - /* 200 - * Only match BIOS date, because the manufacturers 201 - * BIOS does not report the board name at all 202 - * (sometimes)... 203 - */ 204 - DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), 205 - DMI_MATCH(DMI_BIOS_DATE, "10/28/2015"), 206 - }, 207 - }, 208 - { 209 - .ident = "WinBook TW100", 210 - .matches = { 211 - DMI_MATCH(DMI_SYS_VENDOR, "WinBook"), 212 - DMI_MATCH(DMI_PRODUCT_NAME, "TW100") 213 - } 214 - }, 215 - { 216 - .ident = "WinBook TW700", 217 - .matches = { 218 - DMI_MATCH(DMI_SYS_VENDOR, "WinBook"), 219 - DMI_MATCH(DMI_PRODUCT_NAME, "TW700") 220 - }, 221 - }, 222 - #endif 223 - {} 224 - }; 225 - 226 181 static const struct dmi_system_id nine_bytes_report[] = { 227 182 #if defined(CONFIG_DMI) && defined(CONFIG_X86) 228 183 { ··· 1076 1121 ABS_MT_POSITION_X, ts->prop.max_x); 1077 1122 input_abs_set_max(ts->input_dev, 1078 1123 ABS_MT_POSITION_Y, ts->prop.max_y); 1079 - } 1080 - 1081 - if (dmi_check_system(rotated_screen)) { 1082 - ts->prop.invert_x = true; 1083 - ts->prop.invert_y = true; 1084 - dev_dbg(&ts->client->dev, 1085 - "Applying '180 degrees rotated screen' quirk\n"); 1086 1124 } 1087 1125 1088 1126 if (dmi_check_system(nine_bytes_report)) {
-18
drivers/mfd/Kconfig
··· 1499 1499 This driver can also be built as a module. If so, the module 1500 1500 will be called tps65217. 1501 1501 1502 - config MFD_TPS68470 1503 - bool "TI TPS68470 Power Management / LED chips" 1504 - depends on ACPI && PCI && I2C=y 1505 - depends on I2C_DESIGNWARE_PLATFORM=y 1506 - select MFD_CORE 1507 - select REGMAP_I2C 1508 - help 1509 - If you say yes here you get support for the TPS68470 series of 1510 - Power Management / LED chips. 1511 - 1512 - These include voltage regulators, LEDs and other features 1513 - that are often used in portable devices. 1514 - 1515 - This option is a bool as it provides an ACPI operation 1516 - region, which must be available before any of the devices 1517 - using this are probed. This option also configures the 1518 - designware-i2c driver to be built-in, for the same reason. 1519 - 1520 1502 config MFD_TI_LP873X 1521 1503 tristate "TI LP873X Power Management IC" 1522 1504 depends on I2C
-1
drivers/mfd/Makefile
··· 105 105 obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o 106 106 obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o 107 107 obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o 108 - obj-$(CONFIG_MFD_TPS68470) += tps68470.o 109 108 obj-$(CONFIG_MFD_TPS80031) += tps80031.o 110 109 obj-$(CONFIG_MENELAUS) += menelaus.o 111 110
-97
drivers/mfd/tps68470.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - /* 3 - * TPS68470 chip Parent driver 4 - * 5 - * Copyright (C) 2017 Intel Corporation 6 - * 7 - * Authors: 8 - * Rajmohan Mani <rajmohan.mani@intel.com> 9 - * Tianshu Qiu <tian.shu.qiu@intel.com> 10 - * Jian Xu Zheng <jian.xu.zheng@intel.com> 11 - * Yuning Pu <yuning.pu@intel.com> 12 - */ 13 - 14 - #include <linux/acpi.h> 15 - #include <linux/delay.h> 16 - #include <linux/i2c.h> 17 - #include <linux/init.h> 18 - #include <linux/mfd/core.h> 19 - #include <linux/mfd/tps68470.h> 20 - #include <linux/regmap.h> 21 - 22 - static const struct mfd_cell tps68470s[] = { 23 - { .name = "tps68470-gpio" }, 24 - { .name = "tps68470_pmic_opregion" }, 25 - }; 26 - 27 - static const struct regmap_config tps68470_regmap_config = { 28 - .reg_bits = 8, 29 - .val_bits = 8, 30 - .max_register = TPS68470_REG_MAX, 31 - }; 32 - 33 - static int tps68470_chip_init(struct device *dev, struct regmap *regmap) 34 - { 35 - unsigned int version; 36 - int ret; 37 - 38 - /* Force software reset */ 39 - ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK); 40 - if (ret) 41 - return ret; 42 - 43 - ret = regmap_read(regmap, TPS68470_REG_REVID, &version); 44 - if (ret) { 45 - dev_err(dev, "Failed to read revision register: %d\n", ret); 46 - return ret; 47 - } 48 - 49 - dev_info(dev, "TPS68470 REVID: 0x%x\n", version); 50 - 51 - return 0; 52 - } 53 - 54 - static int tps68470_probe(struct i2c_client *client) 55 - { 56 - struct device *dev = &client->dev; 57 - struct regmap *regmap; 58 - int ret; 59 - 60 - regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config); 61 - if (IS_ERR(regmap)) { 62 - dev_err(dev, "devm_regmap_init_i2c Error %ld\n", 63 - PTR_ERR(regmap)); 64 - return PTR_ERR(regmap); 65 - } 66 - 67 - i2c_set_clientdata(client, regmap); 68 - 69 - ret = tps68470_chip_init(dev, regmap); 70 - if (ret < 0) { 71 - dev_err(dev, "TPS68470 Init Error %d\n", ret); 72 - return ret; 73 - } 74 - 75 - ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, tps68470s, 76 - ARRAY_SIZE(tps68470s), NULL, 0, NULL); 77 - if (ret < 0) { 78 - dev_err(dev, "devm_mfd_add_devices failed: %d\n", ret); 79 - return ret; 80 - } 81 - 82 - return 0; 83 - } 84 - 85 - static const struct acpi_device_id tps68470_acpi_ids[] = { 86 - {"INT3472"}, 87 - {}, 88 - }; 89 - 90 - static struct i2c_driver tps68470_driver = { 91 - .driver = { 92 - .name = "tps68470", 93 - .acpi_match_table = tps68470_acpi_ids, 94 - }, 95 - .probe_new = tps68470_probe, 96 - }; 97 - builtin_i2c_driver(tps68470_driver);
+1 -1
drivers/platform/surface/aggregator/Kconfig
··· 1 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 4 menuconfig SURFACE_AGGREGATOR 5 5 tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
+1 -1
drivers/platform/surface/aggregator/Makefile
··· 1 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 4 # For include/trace/define_trace.h to include trace.h 5 5 CFLAGS_core.o = -I$(src)
+1 -1
drivers/platform/surface/aggregator/bus.c
··· 2 2 /* 3 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 8 #include <linux/device.h>
+1 -1
drivers/platform/surface/aggregator/bus.h
··· 2 2 /* 3 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 8 #ifndef _SURFACE_AGGREGATOR_BUS_H
+271 -61
drivers/platform/surface/aggregator/controller.c
··· 2 2 /* 3 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 8 #include <linux/acpi.h> ··· 405 405 } 406 406 407 407 return NULL; 408 + } 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 + */ 422 + static void ssam_nf_refcount_dec_free(struct ssam_nf *nf, 423 + struct ssam_event_registry reg, 424 + struct ssam_event_id id) 425 + { 426 + struct ssam_nf_refcount_entry *entry; 427 + 428 + lockdep_assert_held(&nf->lock); 429 + 430 + entry = ssam_nf_refcount_dec(nf, reg, id); 431 + if (entry && entry->refcount == 0) 432 + kfree(entry); 408 433 } 409 434 410 435 /** ··· 2148 2123 /* -- Top-level event registry interface. ----------------------------------- */ 2149 2124 2150 2125 /** 2126 + * ssam_nf_refcount_enable() - Enable event for reference count entry if it has 2127 + * not already been enabled. 2128 + * @ctrl: The controller to enable the event on. 2129 + * @entry: The reference count entry for the event to be enabled. 2130 + * @flags: The flags used for enabling the event on the EC. 2131 + * 2132 + * Enable the event associated with the given reference count entry if the 2133 + * reference count equals one, i.e. the event has not previously been enabled. 2134 + * If the event has already been enabled (i.e. reference count not equal to 2135 + * one), check that the flags used for enabling match and warn about this if 2136 + * they do not. 2137 + * 2138 + * This does not modify the reference count itself, which is done with 2139 + * ssam_nf_refcount_inc() / ssam_nf_refcount_dec(). 2140 + * 2141 + * Note: ``nf->lock`` must be held when calling this function. 2142 + * 2143 + * Return: Returns zero on success. If the event is enabled by this call, 2144 + * returns the status of the event-enable EC command. 2145 + */ 2146 + static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, 2147 + struct ssam_nf_refcount_entry *entry, u8 flags) 2148 + { 2149 + const struct ssam_event_registry reg = entry->key.reg; 2150 + const struct ssam_event_id id = entry->key.id; 2151 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2152 + int status; 2153 + 2154 + lockdep_assert_held(&nf->lock); 2155 + 2156 + ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2157 + reg.target_category, id.target_category, id.instance, entry->refcount); 2158 + 2159 + if (entry->refcount == 1) { 2160 + status = ssam_ssh_event_enable(ctrl, reg, id, flags); 2161 + if (status) 2162 + return status; 2163 + 2164 + entry->flags = flags; 2165 + 2166 + } else if (entry->flags != flags) { 2167 + ssam_warn(ctrl, 2168 + "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2169 + flags, entry->flags, reg.target_category, id.target_category, 2170 + id.instance); 2171 + } 2172 + 2173 + return 0; 2174 + } 2175 + 2176 + /** 2177 + * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is 2178 + * no longer in use and free the corresponding entry. 2179 + * @ctrl: The controller to disable the event on. 2180 + * @entry: The reference count entry for the event to be disabled. 2181 + * @flags: The flags used for enabling the event on the EC. 2182 + * 2183 + * If the reference count equals zero, i.e. the event is no longer requested by 2184 + * any client, the event will be disabled and the corresponding reference count 2185 + * entry freed. The reference count entry must not be used any more after a 2186 + * call to this function. 2187 + * 2188 + * Also checks if the flags used for disabling the event match the flags used 2189 + * for enabling the event and warns if they do not (regardless of reference 2190 + * count). 2191 + * 2192 + * This does not modify the reference count itself, which is done with 2193 + * ssam_nf_refcount_inc() / ssam_nf_refcount_dec(). 2194 + * 2195 + * Note: ``nf->lock`` must be held when calling this function. 2196 + * 2197 + * Return: Returns zero on success. If the event is disabled by this call, 2198 + * returns the status of the event-enable EC command. 2199 + */ 2200 + static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, 2201 + struct ssam_nf_refcount_entry *entry, u8 flags) 2202 + { 2203 + const struct ssam_event_registry reg = entry->key.reg; 2204 + const struct ssam_event_id id = entry->key.id; 2205 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2206 + int status = 0; 2207 + 2208 + lockdep_assert_held(&nf->lock); 2209 + 2210 + ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2211 + reg.target_category, id.target_category, id.instance, entry->refcount); 2212 + 2213 + if (entry->flags != flags) { 2214 + ssam_warn(ctrl, 2215 + "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2216 + flags, entry->flags, reg.target_category, id.target_category, 2217 + id.instance); 2218 + } 2219 + 2220 + if (entry->refcount == 0) { 2221 + status = ssam_ssh_event_disable(ctrl, reg, id, flags); 2222 + kfree(entry); 2223 + } 2224 + 2225 + return status; 2226 + } 2227 + 2228 + /** 2151 2229 * ssam_notifier_register() - Register an event notifier. 2152 2230 * @ctrl: The controller to register the notifier on. 2153 2231 * @n: The event notifier to register. 2154 2232 * 2155 - * Register an event notifier and increment the usage counter of the 2156 - * associated SAM event. If the event was previously not enabled, it will be 2157 - * enabled during this call. 2233 + * Register an event notifier. Increment the usage counter of the associated 2234 + * SAM event if the notifier is not marked as an observer. If the event is not 2235 + * marked as an observer and is currently not enabled, it will be enabled 2236 + * during this call. If the notifier is marked as an observer, no attempt will 2237 + * be made at enabling any event and no reference count will be modified. 2238 + * 2239 + * Notifiers marked as observers do not need to be associated with one specific 2240 + * event, i.e. as long as no event matching is performed, only the event target 2241 + * category needs to be set. 2158 2242 * 2159 2243 * Return: Returns zero on success, %-ENOSPC if there have already been 2160 2244 * %INT_MAX notifiers for the event ID/type associated with the notifier block ··· 2272 2138 * for the specific associated event, returns the status of the event-enable 2273 2139 * EC-command. 2274 2140 */ 2275 - int ssam_notifier_register(struct ssam_controller *ctrl, 2276 - struct ssam_event_notifier *n) 2141 + int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notifier *n) 2277 2142 { 2278 2143 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); 2279 - struct ssam_nf_refcount_entry *entry; 2144 + struct ssam_nf_refcount_entry *entry = NULL; 2280 2145 struct ssam_nf_head *nf_head; 2281 2146 struct ssam_nf *nf; 2282 2147 int status; ··· 2288 2155 2289 2156 mutex_lock(&nf->lock); 2290 2157 2291 - entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id); 2292 - if (IS_ERR(entry)) { 2293 - mutex_unlock(&nf->lock); 2294 - return PTR_ERR(entry); 2158 + if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) { 2159 + entry = ssam_nf_refcount_inc(nf, n->event.reg, n->event.id); 2160 + if (IS_ERR(entry)) { 2161 + mutex_unlock(&nf->lock); 2162 + return PTR_ERR(entry); 2163 + } 2295 2164 } 2296 - 2297 - ssam_dbg(ctrl, "enabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2298 - n->event.reg.target_category, n->event.id.target_category, 2299 - n->event.id.instance, entry->refcount); 2300 2165 2301 2166 status = ssam_nfblk_insert(nf_head, &n->base); 2302 2167 if (status) { 2303 - entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); 2304 - if (entry->refcount == 0) 2305 - kfree(entry); 2168 + if (entry) 2169 + ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id); 2306 2170 2307 2171 mutex_unlock(&nf->lock); 2308 2172 return status; 2309 2173 } 2310 2174 2311 - if (entry->refcount == 1) { 2312 - status = ssam_ssh_event_enable(ctrl, n->event.reg, n->event.id, 2313 - n->event.flags); 2175 + if (entry) { 2176 + status = ssam_nf_refcount_enable(ctrl, entry, n->event.flags); 2314 2177 if (status) { 2315 2178 ssam_nfblk_remove(&n->base); 2316 - kfree(ssam_nf_refcount_dec(nf, n->event.reg, n->event.id)); 2179 + ssam_nf_refcount_dec_free(nf, n->event.reg, n->event.id); 2317 2180 mutex_unlock(&nf->lock); 2318 2181 synchronize_srcu(&nf_head->srcu); 2319 2182 return status; 2320 2183 } 2321 - 2322 - entry->flags = n->event.flags; 2323 - 2324 - } else if (entry->flags != n->event.flags) { 2325 - ssam_warn(ctrl, 2326 - "inconsistent flags when enabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2327 - n->event.flags, entry->flags, n->event.reg.target_category, 2328 - n->event.id.target_category, n->event.id.instance); 2329 2184 } 2330 2185 2331 2186 mutex_unlock(&nf->lock); ··· 2326 2205 * @ctrl: The controller the notifier has been registered on. 2327 2206 * @n: The event notifier to unregister. 2328 2207 * 2329 - * Unregister an event notifier and decrement the usage counter of the 2330 - * associated SAM event. If the usage counter reaches zero, the event will be 2331 - * disabled. 2208 + * Unregister an event notifier. Decrement the usage counter of the associated 2209 + * SAM event if the notifier is not marked as an observer. If the usage counter 2210 + * reaches zero, the event will be disabled. 2332 2211 * 2333 2212 * Return: Returns zero on success, %-ENOENT if the given notifier block has 2334 2213 * not been registered on the controller. If the given notifier block was the 2335 2214 * last one associated with its specific event, returns the status of the 2336 2215 * event-disable EC-command. 2337 2216 */ 2338 - int ssam_notifier_unregister(struct ssam_controller *ctrl, 2339 - struct ssam_event_notifier *n) 2217 + int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n) 2340 2218 { 2341 2219 u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); 2342 2220 struct ssam_nf_refcount_entry *entry; ··· 2356 2236 return -ENOENT; 2357 2237 } 2358 2238 2359 - entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); 2360 - if (WARN_ON(!entry)) { 2361 - /* 2362 - * If this does not return an entry, there's a logic error 2363 - * somewhere: The notifier block is registered, but the event 2364 - * refcount entry is not there. Remove the notifier block 2365 - * anyways. 2366 - */ 2367 - status = -ENOENT; 2368 - goto remove; 2369 - } 2239 + /* 2240 + * If this is an observer notifier, do not attempt to disable the 2241 + * event, just remove it. 2242 + */ 2243 + if (!(n->flags & SSAM_EVENT_NOTIFIER_OBSERVER)) { 2244 + entry = ssam_nf_refcount_dec(nf, n->event.reg, n->event.id); 2245 + if (WARN_ON(!entry)) { 2246 + /* 2247 + * If this does not return an entry, there's a logic 2248 + * error somewhere: The notifier block is registered, 2249 + * but the event refcount entry is not there. Remove 2250 + * the notifier block anyways. 2251 + */ 2252 + status = -ENOENT; 2253 + goto remove; 2254 + } 2370 2255 2371 - ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", 2372 - n->event.reg.target_category, n->event.id.target_category, 2373 - n->event.id.instance, entry->refcount); 2374 - 2375 - if (entry->flags != n->event.flags) { 2376 - ssam_warn(ctrl, 2377 - "inconsistent flags when disabling event: got %#04x, expected %#04x (reg: %#04x, tc: %#04x, iid: %#04x)\n", 2378 - n->event.flags, entry->flags, n->event.reg.target_category, 2379 - n->event.id.target_category, n->event.id.instance); 2380 - } 2381 - 2382 - if (entry->refcount == 0) { 2383 - status = ssam_ssh_event_disable(ctrl, n->event.reg, n->event.id, 2384 - n->event.flags); 2385 - kfree(entry); 2256 + status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags); 2386 2257 } 2387 2258 2388 2259 remove: ··· 2384 2273 return status; 2385 2274 } 2386 2275 EXPORT_SYMBOL_GPL(ssam_notifier_unregister); 2276 + 2277 + /** 2278 + * ssam_controller_event_enable() - Enable the specified event. 2279 + * @ctrl: The controller to enable the event for. 2280 + * @reg: The event registry to use for enabling the event. 2281 + * @id: The event ID specifying the event to be enabled. 2282 + * @flags: The SAM event flags used for enabling the event. 2283 + * 2284 + * Increment the event reference count of the specified event. If the event has 2285 + * not been enabled previously, it will be enabled by this call. 2286 + * 2287 + * Note: In general, ssam_notifier_register() with a non-observer notifier 2288 + * should be preferred for enabling/disabling events, as this will guarantee 2289 + * proper ordering and event forwarding in case of errors during event 2290 + * enabling/disabling. 2291 + * 2292 + * Return: Returns zero on success, %-ENOSPC if the reference count for the 2293 + * specified event has reached its maximum, %-ENOMEM if the corresponding event 2294 + * entry could not be allocated. If this is the first time that this event has 2295 + * been enabled (i.e. the reference count was incremented from zero to one by 2296 + * this call), returns the status of the event-enable EC-command. 2297 + */ 2298 + int ssam_controller_event_enable(struct ssam_controller *ctrl, 2299 + struct ssam_event_registry reg, 2300 + struct ssam_event_id id, u8 flags) 2301 + { 2302 + u16 rqid = ssh_tc_to_rqid(id.target_category); 2303 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2304 + struct ssam_nf_refcount_entry *entry; 2305 + int status; 2306 + 2307 + if (!ssh_rqid_is_event(rqid)) 2308 + return -EINVAL; 2309 + 2310 + mutex_lock(&nf->lock); 2311 + 2312 + entry = ssam_nf_refcount_inc(nf, reg, id); 2313 + if (IS_ERR(entry)) { 2314 + mutex_unlock(&nf->lock); 2315 + return PTR_ERR(entry); 2316 + } 2317 + 2318 + status = ssam_nf_refcount_enable(ctrl, entry, flags); 2319 + if (status) { 2320 + ssam_nf_refcount_dec_free(nf, reg, id); 2321 + mutex_unlock(&nf->lock); 2322 + return status; 2323 + } 2324 + 2325 + mutex_unlock(&nf->lock); 2326 + return 0; 2327 + } 2328 + EXPORT_SYMBOL_GPL(ssam_controller_event_enable); 2329 + 2330 + /** 2331 + * ssam_controller_event_disable() - Disable the specified event. 2332 + * @ctrl: The controller to disable the event for. 2333 + * @reg: The event registry to use for disabling the event. 2334 + * @id: The event ID specifying the event to be disabled. 2335 + * @flags: The flags used when enabling the event. 2336 + * 2337 + * Decrement the reference count of the specified event. If the reference count 2338 + * reaches zero, the event will be disabled. 2339 + * 2340 + * Note: In general, ssam_notifier_register()/ssam_notifier_unregister() with a 2341 + * non-observer notifier should be preferred for enabling/disabling events, as 2342 + * this will guarantee proper ordering and event forwarding in case of errors 2343 + * during event enabling/disabling. 2344 + * 2345 + * Return: Returns zero on success, %-ENOENT if the given event has not been 2346 + * enabled on the controller. If the reference count of the event reaches zero 2347 + * during this call, returns the status of the event-disable EC-command. 2348 + */ 2349 + int ssam_controller_event_disable(struct ssam_controller *ctrl, 2350 + struct ssam_event_registry reg, 2351 + struct ssam_event_id id, u8 flags) 2352 + { 2353 + u16 rqid = ssh_tc_to_rqid(id.target_category); 2354 + struct ssam_nf *nf = &ctrl->cplt.event.notif; 2355 + struct ssam_nf_refcount_entry *entry; 2356 + int status; 2357 + 2358 + if (!ssh_rqid_is_event(rqid)) 2359 + return -EINVAL; 2360 + 2361 + mutex_lock(&nf->lock); 2362 + 2363 + entry = ssam_nf_refcount_dec(nf, reg, id); 2364 + if (!entry) { 2365 + mutex_unlock(&nf->lock); 2366 + return -ENOENT; 2367 + } 2368 + 2369 + status = ssam_nf_refcount_disable_free(ctrl, entry, flags); 2370 + 2371 + mutex_unlock(&nf->lock); 2372 + return status; 2373 + } 2374 + EXPORT_SYMBOL_GPL(ssam_controller_event_disable); 2387 2375 2388 2376 /** 2389 2377 * ssam_notifier_disable_registered() - Disable events for all registered
+1 -1
drivers/platform/surface/aggregator/controller.h
··· 2 2 /* 3 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 8 #ifndef _SURFACE_AGGREGATOR_CONTROLLER_H
+1 -1
drivers/platform/surface/aggregator/core.c
··· 7 7 * Handles communication via requests as well as enabling, disabling, and 8 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 13 #include <linux/acpi.h>
+1 -1
drivers/platform/surface/aggregator/ssh_msgb.h
··· 2 2 /* 3 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 8 #ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H
+4 -8
drivers/platform/surface/aggregator/ssh_packet_layer.c
··· 2 2 /* 3 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 8 #include <asm/unaligned.h> ··· 1567 1567 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state); 1568 1568 1569 1569 atomic_dec(&ptl->pending.count); 1570 - list_del(&p->pending_node); 1571 - 1572 - list_add_tail(&p->pending_node, &claimed); 1570 + list_move_tail(&p->pending_node, &claimed); 1573 1571 } 1574 1572 1575 1573 spin_unlock(&ptl->pending.lock); ··· 1955 1957 smp_mb__before_atomic(); 1956 1958 clear_bit(SSH_PACKET_SF_QUEUED_BIT, &p->state); 1957 1959 1958 - list_del(&p->queue_node); 1959 - list_add_tail(&p->queue_node, &complete_q); 1960 + list_move_tail(&p->queue_node, &complete_q); 1960 1961 } 1961 1962 spin_unlock(&ptl->queue.lock); 1962 1963 ··· 1967 1970 smp_mb__before_atomic(); 1968 1971 clear_bit(SSH_PACKET_SF_PENDING_BIT, &p->state); 1969 1972 1970 - list_del(&p->pending_node); 1971 - list_add_tail(&p->pending_node, &complete_q); 1973 + list_move_tail(&p->pending_node, &complete_q); 1972 1974 } 1973 1975 atomic_set(&ptl->pending.count, 0); 1974 1976 spin_unlock(&ptl->pending.lock);
+1 -1
drivers/platform/surface/aggregator/ssh_packet_layer.h
··· 2 2 /* 3 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 8 #ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H
+1 -1
drivers/platform/surface/aggregator/ssh_parser.c
··· 2 2 /* 3 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 8 #include <asm/unaligned.h>
+1 -1
drivers/platform/surface/aggregator/ssh_parser.h
··· 2 2 /* 3 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 8 #ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H
+4 -8
drivers/platform/surface/aggregator/ssh_request_layer.c
··· 2 2 /* 3 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 8 #include <asm/unaligned.h> ··· 863 863 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state); 864 864 865 865 atomic_dec(&rtl->pending.count); 866 - list_del(&r->node); 867 - 868 - list_add_tail(&r->node, &claimed); 866 + list_move_tail(&r->node, &claimed); 869 867 } 870 868 spin_unlock(&rtl->pending.lock); 871 869 ··· 1202 1204 smp_mb__before_atomic(); 1203 1205 clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &r->state); 1204 1206 1205 - list_del(&r->node); 1206 - list_add_tail(&r->node, &claimed); 1207 + list_move_tail(&r->node, &claimed); 1207 1208 } 1208 1209 spin_unlock(&rtl->queue.lock); 1209 1210 ··· 1235 1238 smp_mb__before_atomic(); 1236 1239 clear_bit(SSH_REQUEST_SF_PENDING_BIT, &r->state); 1237 1240 1238 - list_del(&r->node); 1239 - list_add_tail(&r->node, &claimed); 1241 + list_move_tail(&r->node, &claimed); 1240 1242 } 1241 1243 spin_unlock(&rtl->pending.lock); 1242 1244 }
+1 -1
drivers/platform/surface/aggregator/ssh_request_layer.h
··· 2 2 /* 3 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 8 #ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H
+1 -1
drivers/platform/surface/aggregator/trace.h
··· 2 2 /* 3 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 8 #undef TRACE_SYSTEM
+512 -24
drivers/platform/surface/surface_aggregator_cdev.c
··· 3 3 * Provides user-space access to the SSAM EC via the /dev/surface/aggregator 4 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 9 #include <linux/fs.h> 10 + #include <linux/ioctl.h> 10 11 #include <linux/kernel.h> 12 + #include <linux/kfifo.h> 11 13 #include <linux/kref.h> 12 14 #include <linux/miscdevice.h> 13 15 #include <linux/module.h> 14 16 #include <linux/platform_device.h> 17 + #include <linux/poll.h> 15 18 #include <linux/rwsem.h> 16 19 #include <linux/slab.h> 17 20 #include <linux/uaccess.h> 21 + #include <linux/vmalloc.h> 18 22 19 23 #include <linux/surface_aggregator/cdev.h> 20 24 #include <linux/surface_aggregator/controller.h> 25 + #include <linux/surface_aggregator/serial_hub.h> 21 26 22 27 #define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev" 28 + 29 + 30 + /* -- Main structures. ------------------------------------------------------ */ 31 + 32 + enum ssam_cdev_device_state { 33 + SSAM_CDEV_DEVICE_SHUTDOWN_BIT = BIT(0), 34 + }; 23 35 24 36 struct ssam_cdev { 25 37 struct kref kref; 26 38 struct rw_semaphore lock; 39 + 40 + struct device *dev; 27 41 struct ssam_controller *ctrl; 28 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 + 49 + struct ssam_cdev_client; 50 + 51 + struct ssam_cdev_notifier { 52 + struct ssam_cdev_client *client; 53 + struct ssam_event_notifier nf; 54 + }; 55 + 56 + struct ssam_cdev_client { 57 + struct ssam_cdev *cdev; 58 + struct list_head node; 59 + 60 + struct mutex notifier_lock; /* Guards notifier access for registration */ 61 + struct ssam_cdev_notifier *notifier[SSH_NUM_EVENTS]; 62 + 63 + struct mutex read_lock; /* Guards FIFO buffer read access */ 64 + struct mutex write_lock; /* Guards FIFO buffer write access */ 65 + DECLARE_KFIFO(buffer, u8, 4096); 66 + 67 + wait_queue_head_t waitq; 68 + struct fasync_struct *fasync; 29 69 }; 30 70 31 71 static void __ssam_cdev_release(struct kref *kref) ··· 87 47 kref_put(&cdev->kref, __ssam_cdev_release); 88 48 } 89 49 90 - static int ssam_cdev_device_open(struct inode *inode, struct file *filp) 91 - { 92 - struct miscdevice *mdev = filp->private_data; 93 - struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev); 94 50 95 - filp->private_data = ssam_cdev_get(cdev); 96 - return stream_open(inode, filp); 97 - } 51 + /* -- Notifier handling. ---------------------------------------------------- */ 98 52 99 - static int ssam_cdev_device_release(struct inode *inode, struct file *filp) 53 + static u32 ssam_cdev_notifier(struct ssam_event_notifier *nf, const struct ssam_event *in) 100 54 { 101 - ssam_cdev_put(filp->private_data); 55 + struct ssam_cdev_notifier *cdev_nf = container_of(nf, struct ssam_cdev_notifier, nf); 56 + struct ssam_cdev_client *client = cdev_nf->client; 57 + struct ssam_cdev_event event; 58 + size_t n = struct_size(&event, data, in->length); 59 + 60 + /* Translate event. */ 61 + event.target_category = in->target_category; 62 + event.target_id = in->target_id; 63 + event.command_id = in->command_id; 64 + event.instance_id = in->instance_id; 65 + event.length = in->length; 66 + 67 + mutex_lock(&client->write_lock); 68 + 69 + /* Make sure we have enough space. */ 70 + if (kfifo_avail(&client->buffer) < n) { 71 + dev_warn(client->cdev->dev, 72 + "buffer full, dropping event (tc: %#04x, tid: %#04x, cid: %#04x, iid: %#04x)\n", 73 + in->target_category, in->target_id, in->command_id, in->instance_id); 74 + mutex_unlock(&client->write_lock); 75 + return 0; 76 + } 77 + 78 + /* Copy event header and payload. */ 79 + kfifo_in(&client->buffer, (const u8 *)&event, struct_size(&event, data, 0)); 80 + kfifo_in(&client->buffer, &in->data[0], in->length); 81 + 82 + mutex_unlock(&client->write_lock); 83 + 84 + /* Notify waiting readers. */ 85 + kill_fasync(&client->fasync, SIGIO, POLL_IN); 86 + wake_up_interruptible(&client->waitq); 87 + 88 + /* 89 + * Don't mark events as handled, this is the job of a proper driver and 90 + * not the debugging interface. 91 + */ 102 92 return 0; 103 93 } 104 94 105 - static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg) 95 + static int ssam_cdev_notifier_register(struct ssam_cdev_client *client, u8 tc, int priority) 106 96 { 107 - struct ssam_cdev_request __user *r; 97 + const u16 rqid = ssh_tc_to_rqid(tc); 98 + const u16 event = ssh_rqid_to_event(rqid); 99 + struct ssam_cdev_notifier *nf; 100 + int status; 101 + 102 + lockdep_assert_held_read(&client->cdev->lock); 103 + 104 + /* Validate notifier target category. */ 105 + if (!ssh_rqid_is_event(rqid)) 106 + return -EINVAL; 107 + 108 + mutex_lock(&client->notifier_lock); 109 + 110 + /* Check if the notifier has already been registered. */ 111 + if (client->notifier[event]) { 112 + mutex_unlock(&client->notifier_lock); 113 + return -EEXIST; 114 + } 115 + 116 + /* Allocate new notifier. */ 117 + nf = kzalloc(sizeof(*nf), GFP_KERNEL); 118 + if (!nf) { 119 + mutex_unlock(&client->notifier_lock); 120 + return -ENOMEM; 121 + } 122 + 123 + /* 124 + * Create a dummy notifier with the minimal required fields for 125 + * observer registration. Note that we can skip fully specifying event 126 + * and registry here as we do not need any matching and use silent 127 + * registration, which does not enable the corresponding event. 128 + */ 129 + nf->client = client; 130 + nf->nf.base.fn = ssam_cdev_notifier; 131 + nf->nf.base.priority = priority; 132 + nf->nf.event.id.target_category = tc; 133 + nf->nf.event.mask = 0; /* Do not do any matching. */ 134 + nf->nf.flags = SSAM_EVENT_NOTIFIER_OBSERVER; 135 + 136 + /* Register notifier. */ 137 + status = ssam_notifier_register(client->cdev->ctrl, &nf->nf); 138 + if (status) 139 + kfree(nf); 140 + else 141 + client->notifier[event] = nf; 142 + 143 + mutex_unlock(&client->notifier_lock); 144 + return status; 145 + } 146 + 147 + static int ssam_cdev_notifier_unregister(struct ssam_cdev_client *client, u8 tc) 148 + { 149 + const u16 rqid = ssh_tc_to_rqid(tc); 150 + const u16 event = ssh_rqid_to_event(rqid); 151 + int status; 152 + 153 + lockdep_assert_held_read(&client->cdev->lock); 154 + 155 + /* Validate notifier target category. */ 156 + if (!ssh_rqid_is_event(rqid)) 157 + return -EINVAL; 158 + 159 + mutex_lock(&client->notifier_lock); 160 + 161 + /* Check if the notifier is currently registered. */ 162 + if (!client->notifier[event]) { 163 + mutex_unlock(&client->notifier_lock); 164 + return -ENOENT; 165 + } 166 + 167 + /* Unregister and free notifier. */ 168 + status = ssam_notifier_unregister(client->cdev->ctrl, &client->notifier[event]->nf); 169 + kfree(client->notifier[event]); 170 + client->notifier[event] = NULL; 171 + 172 + mutex_unlock(&client->notifier_lock); 173 + return status; 174 + } 175 + 176 + static void ssam_cdev_notifier_unregister_all(struct ssam_cdev_client *client) 177 + { 178 + int i; 179 + 180 + down_read(&client->cdev->lock); 181 + 182 + /* 183 + * This function may be used during shutdown, thus we need to test for 184 + * cdev->ctrl instead of the SSAM_CDEV_DEVICE_SHUTDOWN_BIT bit. 185 + */ 186 + if (client->cdev->ctrl) { 187 + for (i = 0; i < SSH_NUM_EVENTS; i++) 188 + ssam_cdev_notifier_unregister(client, i + 1); 189 + 190 + } else { 191 + int count = 0; 192 + 193 + /* 194 + * Device has been shut down. Any notifier remaining is a bug, 195 + * so warn about that as this would otherwise hardly be 196 + * noticeable. Nevertheless, free them as well. 197 + */ 198 + mutex_lock(&client->notifier_lock); 199 + for (i = 0; i < SSH_NUM_EVENTS; i++) { 200 + count += !!(client->notifier[i]); 201 + kfree(client->notifier[i]); 202 + client->notifier[i] = NULL; 203 + } 204 + mutex_unlock(&client->notifier_lock); 205 + 206 + WARN_ON(count > 0); 207 + } 208 + 209 + up_read(&client->cdev->lock); 210 + } 211 + 212 + 213 + /* -- IOCTL functions. ------------------------------------------------------ */ 214 + 215 + static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_request __user *r) 216 + { 108 217 struct ssam_cdev_request rqst; 109 218 struct ssam_request spec = {}; 110 219 struct ssam_response rsp = {}; ··· 261 72 void __user *rspdata; 262 73 int status = 0, ret = 0, tmp; 263 74 264 - r = (struct ssam_cdev_request __user *)arg; 75 + lockdep_assert_held_read(&client->cdev->lock); 76 + 265 77 ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r)); 266 78 if (ret) 267 79 goto out; ··· 342 152 } 343 153 344 154 /* Perform request. */ 345 - status = ssam_request_sync(cdev->ctrl, &spec, &rsp); 155 + status = ssam_request_sync(client->cdev->ctrl, &spec, &rsp); 346 156 if (status) 347 157 goto out; 348 158 ··· 367 177 return ret; 368 178 } 369 179 370 - static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd, 180 + static long ssam_cdev_notif_register(struct ssam_cdev_client *client, 181 + const struct ssam_cdev_notifier_desc __user *d) 182 + { 183 + struct ssam_cdev_notifier_desc desc; 184 + long ret; 185 + 186 + lockdep_assert_held_read(&client->cdev->lock); 187 + 188 + ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d)); 189 + if (ret) 190 + return ret; 191 + 192 + return ssam_cdev_notifier_register(client, desc.target_category, desc.priority); 193 + } 194 + 195 + static long ssam_cdev_notif_unregister(struct ssam_cdev_client *client, 196 + const struct ssam_cdev_notifier_desc __user *d) 197 + { 198 + struct ssam_cdev_notifier_desc desc; 199 + long ret; 200 + 201 + lockdep_assert_held_read(&client->cdev->lock); 202 + 203 + ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d)); 204 + if (ret) 205 + return ret; 206 + 207 + return ssam_cdev_notifier_unregister(client, desc.target_category); 208 + } 209 + 210 + static long ssam_cdev_event_enable(struct ssam_cdev_client *client, 211 + const struct ssam_cdev_event_desc __user *d) 212 + { 213 + struct ssam_cdev_event_desc desc; 214 + struct ssam_event_registry reg; 215 + struct ssam_event_id id; 216 + long ret; 217 + 218 + lockdep_assert_held_read(&client->cdev->lock); 219 + 220 + /* Read descriptor from user-space. */ 221 + ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d)); 222 + if (ret) 223 + return ret; 224 + 225 + /* Translate descriptor. */ 226 + reg.target_category = desc.reg.target_category; 227 + reg.target_id = desc.reg.target_id; 228 + reg.cid_enable = desc.reg.cid_enable; 229 + reg.cid_disable = desc.reg.cid_disable; 230 + 231 + id.target_category = desc.id.target_category; 232 + id.instance = desc.id.instance; 233 + 234 + /* Disable event. */ 235 + return ssam_controller_event_enable(client->cdev->ctrl, reg, id, desc.flags); 236 + } 237 + 238 + static long ssam_cdev_event_disable(struct ssam_cdev_client *client, 239 + const struct ssam_cdev_event_desc __user *d) 240 + { 241 + struct ssam_cdev_event_desc desc; 242 + struct ssam_event_registry reg; 243 + struct ssam_event_id id; 244 + long ret; 245 + 246 + lockdep_assert_held_read(&client->cdev->lock); 247 + 248 + /* Read descriptor from user-space. */ 249 + ret = copy_struct_from_user(&desc, sizeof(desc), d, sizeof(*d)); 250 + if (ret) 251 + return ret; 252 + 253 + /* Translate descriptor. */ 254 + reg.target_category = desc.reg.target_category; 255 + reg.target_id = desc.reg.target_id; 256 + reg.cid_enable = desc.reg.cid_enable; 257 + reg.cid_disable = desc.reg.cid_disable; 258 + 259 + id.target_category = desc.id.target_category; 260 + id.instance = desc.id.instance; 261 + 262 + /* Disable event. */ 263 + return ssam_controller_event_disable(client->cdev->ctrl, reg, id, desc.flags); 264 + } 265 + 266 + 267 + /* -- File operations. ------------------------------------------------------ */ 268 + 269 + static int ssam_cdev_device_open(struct inode *inode, struct file *filp) 270 + { 271 + struct miscdevice *mdev = filp->private_data; 272 + struct ssam_cdev_client *client; 273 + struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev); 274 + 275 + /* Initialize client */ 276 + client = vzalloc(sizeof(*client)); 277 + if (!client) 278 + return -ENOMEM; 279 + 280 + client->cdev = ssam_cdev_get(cdev); 281 + 282 + INIT_LIST_HEAD(&client->node); 283 + 284 + mutex_init(&client->notifier_lock); 285 + 286 + mutex_init(&client->read_lock); 287 + mutex_init(&client->write_lock); 288 + INIT_KFIFO(client->buffer); 289 + init_waitqueue_head(&client->waitq); 290 + 291 + filp->private_data = client; 292 + 293 + /* Attach client. */ 294 + down_write(&cdev->client_lock); 295 + 296 + if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) { 297 + up_write(&cdev->client_lock); 298 + mutex_destroy(&client->write_lock); 299 + mutex_destroy(&client->read_lock); 300 + mutex_destroy(&client->notifier_lock); 301 + ssam_cdev_put(client->cdev); 302 + vfree(client); 303 + return -ENODEV; 304 + } 305 + list_add_tail(&client->node, &cdev->client_list); 306 + 307 + up_write(&cdev->client_lock); 308 + 309 + stream_open(inode, filp); 310 + return 0; 311 + } 312 + 313 + static int ssam_cdev_device_release(struct inode *inode, struct file *filp) 314 + { 315 + struct ssam_cdev_client *client = filp->private_data; 316 + 317 + /* Force-unregister all remaining notifiers of this client. */ 318 + ssam_cdev_notifier_unregister_all(client); 319 + 320 + /* Detach client. */ 321 + down_write(&client->cdev->client_lock); 322 + list_del(&client->node); 323 + up_write(&client->cdev->client_lock); 324 + 325 + /* Free client. */ 326 + mutex_destroy(&client->write_lock); 327 + mutex_destroy(&client->read_lock); 328 + 329 + mutex_destroy(&client->notifier_lock); 330 + 331 + ssam_cdev_put(client->cdev); 332 + vfree(client); 333 + 334 + return 0; 335 + } 336 + 337 + static long __ssam_cdev_device_ioctl(struct ssam_cdev_client *client, unsigned int cmd, 371 338 unsigned long arg) 372 339 { 340 + lockdep_assert_held_read(&client->cdev->lock); 341 + 373 342 switch (cmd) { 374 343 case SSAM_CDEV_REQUEST: 375 - return ssam_cdev_request(cdev, arg); 344 + return ssam_cdev_request(client, (struct ssam_cdev_request __user *)arg); 345 + 346 + case SSAM_CDEV_NOTIF_REGISTER: 347 + return ssam_cdev_notif_register(client, 348 + (struct ssam_cdev_notifier_desc __user *)arg); 349 + 350 + case SSAM_CDEV_NOTIF_UNREGISTER: 351 + return ssam_cdev_notif_unregister(client, 352 + (struct ssam_cdev_notifier_desc __user *)arg); 353 + 354 + case SSAM_CDEV_EVENT_ENABLE: 355 + return ssam_cdev_event_enable(client, (struct ssam_cdev_event_desc __user *)arg); 356 + 357 + case SSAM_CDEV_EVENT_DISABLE: 358 + return ssam_cdev_event_disable(client, (struct ssam_cdev_event_desc __user *)arg); 376 359 377 360 default: 378 361 return -ENOTTY; 379 362 } 380 363 } 381 364 382 - static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, 383 - unsigned long arg) 365 + static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 384 366 { 385 - struct ssam_cdev *cdev = file->private_data; 367 + struct ssam_cdev_client *client = file->private_data; 386 368 long status; 387 369 388 370 /* Ensure that controller is valid for as long as we need it. */ 371 + if (down_read_killable(&client->cdev->lock)) 372 + return -ERESTARTSYS; 373 + 374 + if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) { 375 + up_read(&client->cdev->lock); 376 + return -ENODEV; 377 + } 378 + 379 + status = __ssam_cdev_device_ioctl(client, cmd, arg); 380 + 381 + up_read(&client->cdev->lock); 382 + return status; 383 + } 384 + 385 + static ssize_t ssam_cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offs) 386 + { 387 + struct ssam_cdev_client *client = file->private_data; 388 + struct ssam_cdev *cdev = client->cdev; 389 + unsigned int copied; 390 + int status = 0; 391 + 389 392 if (down_read_killable(&cdev->lock)) 390 393 return -ERESTARTSYS; 391 394 392 - if (!cdev->ctrl) { 395 + /* Make sure we're not shut down. */ 396 + if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) { 393 397 up_read(&cdev->lock); 394 398 return -ENODEV; 395 399 } 396 400 397 - status = __ssam_cdev_device_ioctl(cdev, cmd, arg); 401 + do { 402 + /* Check availability, wait if necessary. */ 403 + if (kfifo_is_empty(&client->buffer)) { 404 + up_read(&cdev->lock); 405 + 406 + if (file->f_flags & O_NONBLOCK) 407 + return -EAGAIN; 408 + 409 + status = wait_event_interruptible(client->waitq, 410 + !kfifo_is_empty(&client->buffer) || 411 + test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, 412 + &cdev->flags)); 413 + if (status < 0) 414 + return status; 415 + 416 + if (down_read_killable(&cdev->lock)) 417 + return -ERESTARTSYS; 418 + 419 + /* Need to check that we're not shut down again. */ 420 + if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags)) { 421 + up_read(&cdev->lock); 422 + return -ENODEV; 423 + } 424 + } 425 + 426 + /* Try to read from FIFO. */ 427 + if (mutex_lock_interruptible(&client->read_lock)) { 428 + up_read(&cdev->lock); 429 + return -ERESTARTSYS; 430 + } 431 + 432 + status = kfifo_to_user(&client->buffer, buf, count, &copied); 433 + mutex_unlock(&client->read_lock); 434 + 435 + if (status < 0) { 436 + up_read(&cdev->lock); 437 + return status; 438 + } 439 + 440 + /* We might not have gotten anything, check this here. */ 441 + if (copied == 0 && (file->f_flags & O_NONBLOCK)) { 442 + up_read(&cdev->lock); 443 + return -EAGAIN; 444 + } 445 + } while (copied == 0); 398 446 399 447 up_read(&cdev->lock); 400 - return status; 448 + return copied; 449 + } 450 + 451 + static __poll_t ssam_cdev_poll(struct file *file, struct poll_table_struct *pt) 452 + { 453 + struct ssam_cdev_client *client = file->private_data; 454 + __poll_t events = 0; 455 + 456 + if (test_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &client->cdev->flags)) 457 + return EPOLLHUP | EPOLLERR; 458 + 459 + poll_wait(file, &client->waitq, pt); 460 + 461 + if (!kfifo_is_empty(&client->buffer)) 462 + events |= EPOLLIN | EPOLLRDNORM; 463 + 464 + return events; 465 + } 466 + 467 + static int ssam_cdev_fasync(int fd, struct file *file, int on) 468 + { 469 + struct ssam_cdev_client *client = file->private_data; 470 + 471 + return fasync_helper(fd, file, on, &client->fasync); 401 472 } 402 473 403 474 static const struct file_operations ssam_controller_fops = { 404 475 .owner = THIS_MODULE, 405 476 .open = ssam_cdev_device_open, 406 477 .release = ssam_cdev_device_release, 478 + .read = ssam_cdev_read, 479 + .poll = ssam_cdev_poll, 480 + .fasync = ssam_cdev_fasync, 407 481 .unlocked_ioctl = ssam_cdev_device_ioctl, 408 482 .compat_ioctl = ssam_cdev_device_ioctl, 409 - .llseek = noop_llseek, 483 + .llseek = no_llseek, 410 484 }; 485 + 486 + 487 + /* -- Device and driver setup ----------------------------------------------- */ 411 488 412 489 static int ssam_dbg_device_probe(struct platform_device *pdev) 413 490 { ··· 693 236 kref_init(&cdev->kref); 694 237 init_rwsem(&cdev->lock); 695 238 cdev->ctrl = ctrl; 239 + cdev->dev = &pdev->dev; 696 240 697 241 cdev->mdev.parent = &pdev->dev; 698 242 cdev->mdev.minor = MISC_DYNAMIC_MINOR; 699 243 cdev->mdev.name = "surface_aggregator"; 700 244 cdev->mdev.nodename = "surface/aggregator"; 701 245 cdev->mdev.fops = &ssam_controller_fops; 246 + 247 + init_rwsem(&cdev->client_lock); 248 + INIT_LIST_HEAD(&cdev->client_list); 702 249 703 250 status = misc_register(&cdev->mdev); 704 251 if (status) { ··· 717 256 static int ssam_dbg_device_remove(struct platform_device *pdev) 718 257 { 719 258 struct ssam_cdev *cdev = platform_get_drvdata(pdev); 259 + struct ssam_cdev_client *client; 720 260 721 - misc_deregister(&cdev->mdev); 261 + /* 262 + * Mark device as shut-down. Prevent new clients from being added and 263 + * new operations from being executed. 264 + */ 265 + set_bit(SSAM_CDEV_DEVICE_SHUTDOWN_BIT, &cdev->flags); 266 + 267 + down_write(&cdev->client_lock); 268 + 269 + /* Remove all notifiers registered by us. */ 270 + list_for_each_entry(client, &cdev->client_list, node) { 271 + ssam_cdev_notifier_unregister_all(client); 272 + } 273 + 274 + /* Wake up async clients. */ 275 + list_for_each_entry(client, &cdev->client_list, node) { 276 + kill_fasync(&client->fasync, SIGIO, POLL_HUP); 277 + } 278 + 279 + /* Wake up blocking clients. */ 280 + list_for_each_entry(client, &cdev->client_list, node) { 281 + wake_up_interruptible(&client->waitq); 282 + } 283 + 284 + up_write(&cdev->client_lock); 722 285 723 286 /* 724 287 * The controller is only guaranteed to be valid for as long as the ··· 751 266 */ 752 267 down_write(&cdev->lock); 753 268 cdev->ctrl = NULL; 269 + cdev->dev = NULL; 754 270 up_write(&cdev->lock); 271 + 272 + misc_deregister(&cdev->mdev); 755 273 756 274 ssam_cdev_put(cdev); 757 275 return 0;
+12 -35
drivers/platform/surface/surface_aggregator_registry.c
··· 119 119 .parent = &ssam_node_hub_base, 120 120 }; 121 121 122 - /* Devices for Surface Book 2. */ 123 - static const struct software_node *ssam_node_group_sb2[] = { 122 + /* 123 + * Devices for 5th- and 6th-generations models: 124 + * - Surface Book 2, 125 + * - Surface Laptop 1 and 2, 126 + * - Surface Pro 5 and 6. 127 + */ 128 + static const struct software_node *ssam_node_group_gen5[] = { 124 129 &ssam_node_root, 125 130 &ssam_node_tmp_pprof, 126 131 NULL, ··· 147 142 NULL, 148 143 }; 149 144 150 - /* Devices for Surface Laptop 1. */ 151 - static const struct software_node *ssam_node_group_sl1[] = { 152 - &ssam_node_root, 153 - &ssam_node_tmp_pprof, 154 - NULL, 155 - }; 156 - 157 - /* Devices for Surface Laptop 2. */ 158 - static const struct software_node *ssam_node_group_sl2[] = { 159 - &ssam_node_root, 160 - &ssam_node_tmp_pprof, 161 - NULL, 162 - }; 163 - 164 145 /* Devices for Surface Laptop 3 and 4. */ 165 146 static const struct software_node *ssam_node_group_sl3[] = { 166 147 &ssam_node_root, ··· 164 173 &ssam_node_root, 165 174 &ssam_node_bat_ac, 166 175 &ssam_node_bat_main, 167 - &ssam_node_tmp_pprof, 168 - NULL, 169 - }; 170 - 171 - /* Devices for Surface Pro 5. */ 172 - static const struct software_node *ssam_node_group_sp5[] = { 173 - &ssam_node_root, 174 - &ssam_node_tmp_pprof, 175 - NULL, 176 - }; 177 - 178 - /* Devices for Surface Pro 6. */ 179 - static const struct software_node *ssam_node_group_sp6[] = { 180 - &ssam_node_root, 181 176 &ssam_node_tmp_pprof, 182 177 NULL, 183 178 }; ··· 472 495 473 496 static const struct acpi_device_id ssam_platform_hub_match[] = { 474 497 /* Surface Pro 4, 5, and 6 (OMBR < 0x10) */ 475 - { "MSHW0081", (unsigned long)ssam_node_group_sp5 }, 498 + { "MSHW0081", (unsigned long)ssam_node_group_gen5 }, 476 499 477 500 /* Surface Pro 6 (OMBR >= 0x10) */ 478 - { "MSHW0111", (unsigned long)ssam_node_group_sp6 }, 501 + { "MSHW0111", (unsigned long)ssam_node_group_gen5 }, 479 502 480 503 /* Surface Pro 7 */ 481 504 { "MSHW0116", (unsigned long)ssam_node_group_sp7 }, ··· 484 507 { "MSHW0119", (unsigned long)ssam_node_group_sp7 }, 485 508 486 509 /* Surface Book 2 */ 487 - { "MSHW0107", (unsigned long)ssam_node_group_sb2 }, 510 + { "MSHW0107", (unsigned long)ssam_node_group_gen5 }, 488 511 489 512 /* Surface Book 3 */ 490 513 { "MSHW0117", (unsigned long)ssam_node_group_sb3 }, 491 514 492 515 /* Surface Laptop 1 */ 493 - { "MSHW0086", (unsigned long)ssam_node_group_sl1 }, 516 + { "MSHW0086", (unsigned long)ssam_node_group_gen5 }, 494 517 495 518 /* Surface Laptop 2 */ 496 - { "MSHW0112", (unsigned long)ssam_node_group_sl2 }, 519 + { "MSHW0112", (unsigned long)ssam_node_group_gen5 }, 497 520 498 521 /* Surface Laptop 3 (13", Intel) */ 499 522 { "MSHW0114", (unsigned long)ssam_node_group_sl3 },
+21 -28
drivers/platform/x86/Kconfig
··· 415 415 To compile this driver as a module, choose M here: the module will 416 416 be called hp_accel. 417 417 418 - config HP_WIRELESS 419 - tristate "HP wireless button" 418 + config WIRELESS_HOTKEY 419 + tristate "Wireless hotkey button" 420 420 depends on ACPI 421 421 depends on INPUT 422 422 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 425 On such systems the driver should load automatically (via ACPI alias). 425 426 426 427 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 429 430 config HP_WMI 430 431 tristate "HP WMI extras" ··· 640 639 If you are not sure, say Y here. The driver enables polling only if 641 640 it is strictly necessary to do so. 642 641 642 + config THINKPAD_LMI 643 + tristate "Lenovo WMI-based systems management driver" 644 + depends on ACPI_WMI 645 + select FW_ATTR_CLASS 646 + help 647 + This driver allows changing BIOS settings on Lenovo machines whose 648 + BIOS support the WMI interface. 649 + 650 + To compile this driver as a module, choose M here: the module will 651 + be called think-lmi. 652 + 653 + source "drivers/platform/x86/intel/Kconfig" 654 + 643 655 config INTEL_ATOMISP2_LED 644 656 tristate "Intel AtomISP2 camera LED driver" 645 657 depends on GPIOLIB && LEDS_GPIO ··· 686 672 687 673 To compile this driver as a module, choose M here: the module 688 674 will be called intel_atomisp2_pm. 689 - 690 - config INTEL_CHT_INT33FE 691 - tristate "Intel Cherry Trail ACPI INT33FE Driver" 692 - depends on X86 && ACPI && I2C && REGULATOR 693 - depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m) 694 - depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m) 695 - depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m) 696 - help 697 - This driver add support for the INT33FE ACPI device found on 698 - some Intel Cherry Trail devices. 699 - 700 - There are two kinds of INT33FE ACPI device possible: for hardware 701 - with USB Type-C and Micro-B connectors. This driver supports both. 702 - 703 - The INT33FE ACPI device has a CRS table with I2cSerialBusV2 704 - resources for Fuel Gauge Controller and (in the Type-C variant) 705 - FUSB302 USB Type-C Controller and PI3USB30532 USB switch. 706 - This driver instantiates i2c-clients for these, so that standard 707 - i2c drivers for these chips can bind to the them. 708 - 709 - If you enable this driver it is advised to also select 710 - CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B 711 - device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m 712 - for Type-C device. 713 675 714 676 config INTEL_HID_EVENT 715 677 tristate "INTEL HID Event" ··· 1065 1075 into the tablet model specific version of the driver shipped with the 1066 1076 the OS-image for the device. This option supplies the missing info. 1067 1077 Enable this for x86 tablets with Silead or Chipone touchscreens. 1078 + 1079 + config FW_ATTR_CLASS 1080 + tristate 1068 1081 1069 1082 config INTEL_IMR 1070 1083 bool "Intel Isolated Memory Region support"
+5 -5
drivers/platform/x86/Makefile
··· 52 52 53 53 # Hewlett Packard 54 54 obj-$(CONFIG_HP_ACCEL) += hp_accel.o 55 - obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o 56 55 obj-$(CONFIG_HP_WMI) += hp-wmi.o 57 56 obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 58 57 ··· 63 64 obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o 64 65 obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o 65 66 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 67 + obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o 66 68 67 69 # Intel 70 + obj-$(CONFIG_X86_PLATFORM_DRIVERS_INTEL) += intel/ 71 + 68 72 obj-$(CONFIG_INTEL_ATOMISP2_LED) += intel_atomisp2_led.o 69 73 obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o 70 - obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o 71 - intel_cht_int33fe-objs := intel_cht_int33fe_common.o \ 72 - intel_cht_int33fe_typec.o \ 73 - intel_cht_int33fe_microb.o 74 74 obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o 75 75 obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o 76 76 obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o ··· 110 112 obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o 111 113 112 114 # Platform drivers 115 + obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o 113 116 obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o 114 117 obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o 115 118 obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o 119 + obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o 116 120 117 121 # Intel uncore drivers 118 122 obj-$(CONFIG_INTEL_IPS) += intel_ips.o
-77
drivers/platform/x86/asus-nb-wmi.c
··· 110 110 .wmi_force_als_set = true, 111 111 }; 112 112 113 - static struct quirk_entry quirk_asus_vendor_backlight = { 114 - .wmi_backlight_power = true, 115 - .wmi_backlight_set_devstate = true, 116 - }; 117 - 118 113 static struct quirk_entry quirk_asus_use_kbd_dock_devid = { 119 114 .use_kbd_dock_devid = true, 120 115 }; ··· 419 424 DMI_MATCH(DMI_PRODUCT_NAME, "UX430UNR"), 420 425 }, 421 426 .driver_data = &quirk_asus_forceals, 422 - }, 423 - { 424 - .callback = dmi_matched, 425 - .ident = "ASUSTeK COMPUTER INC. GA401IH", 426 - .matches = { 427 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 428 - DMI_MATCH(DMI_PRODUCT_NAME, "GA401IH"), 429 - }, 430 - .driver_data = &quirk_asus_vendor_backlight, 431 - }, 432 - { 433 - .callback = dmi_matched, 434 - .ident = "ASUSTeK COMPUTER INC. GA401II", 435 - .matches = { 436 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 437 - DMI_MATCH(DMI_PRODUCT_NAME, "GA401II"), 438 - }, 439 - .driver_data = &quirk_asus_vendor_backlight, 440 - }, 441 - { 442 - .callback = dmi_matched, 443 - .ident = "ASUSTeK COMPUTER INC. GA401IU", 444 - .matches = { 445 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 446 - DMI_MATCH(DMI_PRODUCT_NAME, "GA401IU"), 447 - }, 448 - .driver_data = &quirk_asus_vendor_backlight, 449 - }, 450 - { 451 - .callback = dmi_matched, 452 - .ident = "ASUSTeK COMPUTER INC. GA401IV", 453 - .matches = { 454 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 455 - DMI_MATCH(DMI_PRODUCT_NAME, "GA401IV"), 456 - }, 457 - .driver_data = &quirk_asus_vendor_backlight, 458 - }, 459 - { 460 - .callback = dmi_matched, 461 - .ident = "ASUSTeK COMPUTER INC. GA401IVC", 462 - .matches = { 463 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 464 - DMI_MATCH(DMI_PRODUCT_NAME, "GA401IVC"), 465 - }, 466 - .driver_data = &quirk_asus_vendor_backlight, 467 - }, 468 - { 469 - .callback = dmi_matched, 470 - .ident = "ASUSTeK COMPUTER INC. GA502II", 471 - .matches = { 472 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 473 - DMI_MATCH(DMI_PRODUCT_NAME, "GA502II"), 474 - }, 475 - .driver_data = &quirk_asus_vendor_backlight, 476 - }, 477 - { 478 - .callback = dmi_matched, 479 - .ident = "ASUSTeK COMPUTER INC. GA502IU", 480 - .matches = { 481 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 482 - DMI_MATCH(DMI_PRODUCT_NAME, "GA502IU"), 483 - }, 484 - .driver_data = &quirk_asus_vendor_backlight, 485 - }, 486 - { 487 - .callback = dmi_matched, 488 - .ident = "ASUSTeK COMPUTER INC. GA502IV", 489 - .matches = { 490 - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 491 - DMI_MATCH(DMI_PRODUCT_NAME, "GA502IV"), 492 - }, 493 - .driver_data = &quirk_asus_vendor_backlight, 494 427 }, 495 428 { 496 429 .callback = dmi_matched,
+10 -1
drivers/platform/x86/dell/Kconfig
··· 5 5 6 6 menuconfig X86_PLATFORM_DRIVERS_DELL 7 7 bool "Dell X86 Platform Specific Device Drivers" 8 - default n 9 8 depends on X86_PLATFORM_DEVICES 10 9 help 11 10 Say Y here to get to see options for device drivers for various ··· 52 53 depends on BACKLIGHT_CLASS_DEVICE 53 54 depends on ACPI_VIDEO || ACPI_VIDEO = n 54 55 depends on RFKILL || RFKILL = n 56 + depends on DELL_WMI || DELL_WMI = n 55 57 depends on SERIO_I8042 56 58 depends on DELL_SMBIOS 57 59 select POWER_SUPPLY ··· 164 164 To compile this driver as a module, choose M here: the module will 165 165 be called dell-wmi. 166 166 167 + config DELL_WMI_PRIVACY 168 + bool "Dell WMI Hardware Privacy Support" 169 + depends on DELL_WMI 170 + depends on LEDS_TRIGGER_AUDIO 171 + help 172 + This option adds integration with the "Dell Hardware Privacy" 173 + feature of Dell laptops to the dell-wmi driver. 174 + 167 175 config DELL_WMI_AIO 168 176 tristate "WMI Hotkeys for Dell All-In-One series" 169 177 default m ··· 205 197 depends on ACPI_WMI 206 198 depends on DMI 207 199 select NLS 200 + select FW_ATTR_CLASS 208 201 help 209 202 This driver allows changing BIOS settings on many Dell machines from 210 203 2018 and newer without the use of any additional software.
+2
drivers/platform/x86/dell/Makefile
··· 15 15 dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o 16 16 obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o 17 17 obj-$(CONFIG_DELL_WMI) += dell-wmi.o 18 + dell-wmi-objs := dell-wmi-base.o 19 + dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o 18 20 obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o 19 21 obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o 20 22 obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
+1 -2
drivers/platform/x86/dell/dcdbas.c
··· 394 394 395 395 /* wait a few to see if it executed */ 396 396 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; 397 - while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) 398 - == ESM_STATUS_CMD_UNSUCCESSFUL) { 397 + while ((s8)inb(PCAT_APM_STATUS_PORT) == ESM_STATUS_CMD_UNSUCCESSFUL) { 399 398 num_ticks--; 400 399 if (num_ticks == EXPIRED_TIMER) 401 400 return -ETIME;
+10 -3
drivers/platform/x86/dell/dell-laptop.c
··· 31 31 #include "dell-rbtn.h" 32 32 #include "dell-smbios.h" 33 33 34 + #include "dell-wmi-privacy.h" 35 + 34 36 struct quirk_entry { 35 37 bool touchpad_led; 36 38 bool kbd_led_not_present; ··· 92 90 static struct rfkill *bluetooth_rfkill; 93 91 static struct rfkill *wwan_rfkill; 94 92 static bool force_rfkill; 93 + static bool micmute_led_registered; 95 94 96 95 module_param(force_rfkill, bool, 0444); 97 96 MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models"); ··· 2208 2205 dell_laptop_register_notifier(&dell_laptop_notifier); 2209 2206 2210 2207 if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) && 2211 - dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) { 2208 + dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) && 2209 + !dell_privacy_has_mic_mute()) { 2212 2210 micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); 2213 2211 ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev); 2214 2212 if (ret < 0) 2215 2213 goto fail_led; 2214 + micmute_led_registered = true; 2216 2215 } 2217 2216 2218 2217 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) ··· 2262 2257 fail_get_brightness: 2263 2258 backlight_device_unregister(dell_backlight_device); 2264 2259 fail_backlight: 2265 - led_classdev_unregister(&micmute_led_cdev); 2260 + if (micmute_led_registered) 2261 + led_classdev_unregister(&micmute_led_cdev); 2266 2262 fail_led: 2267 2263 dell_cleanup_rfkill(); 2268 2264 fail_rfkill: ··· 2284 2278 touchpad_led_exit(); 2285 2279 kbd_led_exit(); 2286 2280 backlight_device_unregister(dell_backlight_device); 2287 - led_classdev_unregister(&micmute_led_cdev); 2281 + if (micmute_led_registered) 2282 + led_classdev_unregister(&micmute_led_cdev); 2288 2283 dell_cleanup_rfkill(); 2289 2284 if (platform_device) { 2290 2285 platform_device_unregister(platform_device);
+391
drivers/platform/x86/dell/dell-wmi-privacy.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Dell privacy notification driver 4 + * 5 + * Copyright (C) 2021 Dell Inc. All Rights Reserved. 6 + */ 7 + 8 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 + 10 + #include <linux/acpi.h> 11 + #include <linux/bitops.h> 12 + #include <linux/input.h> 13 + #include <linux/input/sparse-keymap.h> 14 + #include <linux/list.h> 15 + #include <linux/leds.h> 16 + #include <linux/module.h> 17 + #include <linux/wmi.h> 18 + 19 + #include "dell-wmi-privacy.h" 20 + 21 + #define DELL_PRIVACY_GUID "6932965F-1671-4CEB-B988-D3AB0A901919" 22 + #define MICROPHONE_STATUS BIT(0) 23 + #define CAMERA_STATUS BIT(1) 24 + #define DELL_PRIVACY_AUDIO_EVENT 0x1 25 + #define DELL_PRIVACY_CAMERA_EVENT 0x2 26 + #define led_to_priv(c) container_of(c, struct privacy_wmi_data, cdev) 27 + 28 + /* 29 + * The wmi_list is used to store the privacy_priv struct with mutex protecting 30 + */ 31 + static LIST_HEAD(wmi_list); 32 + static DEFINE_MUTEX(list_mutex); 33 + 34 + struct privacy_wmi_data { 35 + struct input_dev *input_dev; 36 + struct wmi_device *wdev; 37 + struct list_head list; 38 + struct led_classdev cdev; 39 + u32 features_present; 40 + u32 last_status; 41 + }; 42 + 43 + /* DELL Privacy Type */ 44 + enum dell_hardware_privacy_type { 45 + DELL_PRIVACY_TYPE_AUDIO = 0, 46 + DELL_PRIVACY_TYPE_CAMERA, 47 + DELL_PRIVACY_TYPE_SCREEN, 48 + DELL_PRIVACY_TYPE_MAX, 49 + }; 50 + 51 + static const char * const privacy_types[DELL_PRIVACY_TYPE_MAX] = { 52 + [DELL_PRIVACY_TYPE_AUDIO] = "Microphone", 53 + [DELL_PRIVACY_TYPE_CAMERA] = "Camera Shutter", 54 + [DELL_PRIVACY_TYPE_SCREEN] = "ePrivacy Screen", 55 + }; 56 + 57 + /* 58 + * Keymap for WMI privacy events of type 0x0012 59 + */ 60 + static const struct key_entry dell_wmi_keymap_type_0012[] = { 61 + /* privacy mic mute */ 62 + { KE_KEY, 0x0001, { KEY_MICMUTE } }, 63 + /* privacy camera mute */ 64 + { KE_SW, 0x0002, { SW_CAMERA_LENS_COVER } }, 65 + { KE_END, 0}, 66 + }; 67 + 68 + bool dell_privacy_has_mic_mute(void) 69 + { 70 + struct privacy_wmi_data *priv; 71 + 72 + mutex_lock(&list_mutex); 73 + priv = list_first_entry_or_null(&wmi_list, 74 + struct privacy_wmi_data, 75 + list); 76 + mutex_unlock(&list_mutex); 77 + 78 + return priv && (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO)); 79 + } 80 + EXPORT_SYMBOL_GPL(dell_privacy_has_mic_mute); 81 + 82 + /* 83 + * The flow of privacy event: 84 + * 1) User presses key. HW does stuff with this key (timeout is started) 85 + * 2) WMI event is emitted from BIOS 86 + * 3) WMI event is received by dell-privacy 87 + * 4) KEY_MICMUTE emitted from dell-privacy 88 + * 5) Userland picks up key and modifies kcontrol for SW mute 89 + * 6) Codec kernel driver catches and calls ledtrig_audio_set which will call 90 + * led_set_brightness() on the LED registered by dell_privacy_leds_setup() 91 + * 7) dell-privacy notifies EC, the timeout is cancelled and the HW mute activates. 92 + * If the EC is not notified then the HW mic mute will activate when the timeout 93 + * triggers, just a bit later than with the active ack. 94 + */ 95 + bool dell_privacy_process_event(int type, int code, int status) 96 + { 97 + struct privacy_wmi_data *priv; 98 + const struct key_entry *key; 99 + bool ret = false; 100 + 101 + mutex_lock(&list_mutex); 102 + priv = list_first_entry_or_null(&wmi_list, 103 + struct privacy_wmi_data, 104 + list); 105 + if (!priv) 106 + goto error; 107 + 108 + key = sparse_keymap_entry_from_scancode(priv->input_dev, (type << 16) | code); 109 + if (!key) { 110 + dev_warn(&priv->wdev->dev, "Unknown key with type 0x%04x and code 0x%04x pressed\n", 111 + type, code); 112 + goto error; 113 + } 114 + dev_dbg(&priv->wdev->dev, "Key with type 0x%04x and code 0x%04x pressed\n", type, code); 115 + 116 + switch (code) { 117 + case DELL_PRIVACY_AUDIO_EVENT: /* Mic mute */ 118 + case DELL_PRIVACY_CAMERA_EVENT: /* Camera mute */ 119 + priv->last_status = status; 120 + sparse_keymap_report_entry(priv->input_dev, key, 1, true); 121 + ret = true; 122 + break; 123 + default: 124 + dev_dbg(&priv->wdev->dev, "unknown event type 0x%04x 0x%04x\n", type, code); 125 + } 126 + 127 + error: 128 + mutex_unlock(&list_mutex); 129 + return ret; 130 + } 131 + 132 + static ssize_t dell_privacy_supported_type_show(struct device *dev, 133 + struct device_attribute *attr, 134 + char *buf) 135 + { 136 + struct privacy_wmi_data *priv = dev_get_drvdata(dev); 137 + enum dell_hardware_privacy_type type; 138 + u32 privacy_list; 139 + int len = 0; 140 + 141 + privacy_list = priv->features_present; 142 + for (type = DELL_PRIVACY_TYPE_AUDIO; type < DELL_PRIVACY_TYPE_MAX; type++) { 143 + if (privacy_list & BIT(type)) 144 + len += sysfs_emit_at(buf, len, "[%s] [supported]\n", privacy_types[type]); 145 + else 146 + len += sysfs_emit_at(buf, len, "[%s] [unsupported]\n", privacy_types[type]); 147 + } 148 + 149 + return len; 150 + } 151 + 152 + static ssize_t dell_privacy_current_state_show(struct device *dev, 153 + struct device_attribute *attr, 154 + char *buf) 155 + { 156 + struct privacy_wmi_data *priv = dev_get_drvdata(dev); 157 + u32 privacy_supported = priv->features_present; 158 + enum dell_hardware_privacy_type type; 159 + u32 privacy_state = priv->last_status; 160 + int len = 0; 161 + 162 + for (type = DELL_PRIVACY_TYPE_AUDIO; type < DELL_PRIVACY_TYPE_MAX; type++) { 163 + if (privacy_supported & BIT(type)) { 164 + if (privacy_state & BIT(type)) 165 + len += sysfs_emit_at(buf, len, "[%s] [unmuted]\n", privacy_types[type]); 166 + else 167 + len += sysfs_emit_at(buf, len, "[%s] [muted]\n", privacy_types[type]); 168 + } 169 + } 170 + 171 + return len; 172 + } 173 + 174 + static DEVICE_ATTR_RO(dell_privacy_supported_type); 175 + static DEVICE_ATTR_RO(dell_privacy_current_state); 176 + 177 + static struct attribute *privacy_attributes[] = { 178 + &dev_attr_dell_privacy_supported_type.attr, 179 + &dev_attr_dell_privacy_current_state.attr, 180 + NULL, 181 + }; 182 + 183 + static const struct attribute_group privacy_attribute_group = { 184 + .attrs = privacy_attributes 185 + }; 186 + 187 + /* 188 + * Describes the Device State class exposed by BIOS which can be consumed by 189 + * various applications interested in knowing the Privacy feature capabilities. 190 + * class DeviceState 191 + * { 192 + * [key, read] string InstanceName; 193 + * [read] boolean ReadOnly; 194 + * 195 + * [WmiDataId(1), read] uint32 DevicesSupported; 196 + * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen 197 + * 198 + * [WmiDataId(2), read] uint32 CurrentState; 199 + * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen 200 + * }; 201 + */ 202 + static int get_current_status(struct wmi_device *wdev) 203 + { 204 + struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev); 205 + union acpi_object *obj_present; 206 + u32 *buffer; 207 + int ret = 0; 208 + 209 + if (!priv) { 210 + dev_err(&wdev->dev, "dell privacy priv is NULL\n"); 211 + return -EINVAL; 212 + } 213 + /* check privacy support features and device states */ 214 + obj_present = wmidev_block_query(wdev, 0); 215 + if (!obj_present) { 216 + dev_err(&wdev->dev, "failed to read Binary MOF\n"); 217 + return -EIO; 218 + } 219 + 220 + if (obj_present->type != ACPI_TYPE_BUFFER) { 221 + dev_err(&wdev->dev, "Binary MOF is not a buffer!\n"); 222 + ret = -EIO; 223 + goto obj_free; 224 + } 225 + /* Although it's not technically a failure, this would lead to 226 + * unexpected behavior 227 + */ 228 + if (obj_present->buffer.length != 8) { 229 + dev_err(&wdev->dev, "Dell privacy buffer has unexpected length (%d)!\n", 230 + obj_present->buffer.length); 231 + ret = -EINVAL; 232 + goto obj_free; 233 + } 234 + buffer = (u32 *)obj_present->buffer.pointer; 235 + priv->features_present = buffer[0]; 236 + priv->last_status = buffer[1]; 237 + 238 + obj_free: 239 + kfree(obj_present); 240 + return ret; 241 + } 242 + 243 + static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev, 244 + enum led_brightness brightness) 245 + { 246 + struct privacy_wmi_data *priv = led_to_priv(led_cdev); 247 + static char *acpi_method = (char *)"ECAK"; 248 + acpi_status status; 249 + acpi_handle handle; 250 + 251 + handle = ec_get_handle(); 252 + if (!handle) 253 + return -EIO; 254 + 255 + if (!acpi_has_method(handle, acpi_method)) 256 + return -EIO; 257 + 258 + status = acpi_evaluate_object(handle, acpi_method, NULL, NULL); 259 + if (ACPI_FAILURE(status)) { 260 + dev_err(&priv->wdev->dev, "Error setting privacy EC ack value: %s\n", 261 + acpi_format_exception(status)); 262 + return -EIO; 263 + } 264 + 265 + return 0; 266 + } 267 + 268 + /* 269 + * Pressing the mute key activates a time delayed circuit to physically cut 270 + * off the mute. The LED is in the same circuit, so it reflects the true 271 + * state of the HW mute. The reason for the EC "ack" is so that software 272 + * can first invoke a SW mute before the HW circuit is cut off. Without SW 273 + * cutting this off first does not affect the time delayed muting or status 274 + * of the LED but there is a possibility of a "popping" noise. 275 + * 276 + * If the EC receives the SW ack, the circuit will be activated before the 277 + * delay completed. 278 + * 279 + * Exposing as an LED device allows the codec drivers notification path to 280 + * EC ACK to work 281 + */ 282 + static int dell_privacy_leds_setup(struct device *dev) 283 + { 284 + struct privacy_wmi_data *priv = dev_get_drvdata(dev); 285 + 286 + priv->cdev.name = "dell-privacy::micmute"; 287 + priv->cdev.max_brightness = 1; 288 + priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set; 289 + priv->cdev.default_trigger = "audio-micmute"; 290 + priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); 291 + return devm_led_classdev_register(dev, &priv->cdev); 292 + } 293 + 294 + static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context) 295 + { 296 + struct privacy_wmi_data *priv; 297 + struct key_entry *keymap; 298 + int ret, i; 299 + 300 + ret = wmi_has_guid(DELL_PRIVACY_GUID); 301 + if (!ret) 302 + pr_debug("Unable to detect available Dell privacy devices!\n"); 303 + 304 + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); 305 + if (!priv) 306 + return -ENOMEM; 307 + 308 + dev_set_drvdata(&wdev->dev, priv); 309 + priv->wdev = wdev; 310 + /* create evdev passing interface */ 311 + priv->input_dev = devm_input_allocate_device(&wdev->dev); 312 + if (!priv->input_dev) 313 + return -ENOMEM; 314 + 315 + /* remap the wmi keymap event to new keymap */ 316 + keymap = kcalloc(ARRAY_SIZE(dell_wmi_keymap_type_0012), 317 + sizeof(struct key_entry), GFP_KERNEL); 318 + if (!keymap) 319 + return -ENOMEM; 320 + 321 + /* remap the keymap code with Dell privacy key type 0x12 as prefix 322 + * KEY_MICMUTE scancode will be reported as 0x120001 323 + */ 324 + for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) { 325 + keymap[i] = dell_wmi_keymap_type_0012[i]; 326 + keymap[i].code |= (0x0012 << 16); 327 + } 328 + ret = sparse_keymap_setup(priv->input_dev, keymap, NULL); 329 + kfree(keymap); 330 + if (ret) 331 + return ret; 332 + 333 + priv->input_dev->dev.parent = &wdev->dev; 334 + priv->input_dev->name = "Dell Privacy Driver"; 335 + priv->input_dev->id.bustype = BUS_HOST; 336 + 337 + ret = input_register_device(priv->input_dev); 338 + if (ret) 339 + return ret; 340 + 341 + ret = get_current_status(priv->wdev); 342 + if (ret) 343 + return ret; 344 + 345 + ret = devm_device_add_group(&wdev->dev, &privacy_attribute_group); 346 + if (ret) 347 + return ret; 348 + 349 + if (priv->features_present & BIT(DELL_PRIVACY_TYPE_AUDIO)) { 350 + ret = dell_privacy_leds_setup(&priv->wdev->dev); 351 + if (ret) 352 + return ret; 353 + } 354 + mutex_lock(&list_mutex); 355 + list_add_tail(&priv->list, &wmi_list); 356 + mutex_unlock(&list_mutex); 357 + return 0; 358 + } 359 + 360 + static void dell_privacy_wmi_remove(struct wmi_device *wdev) 361 + { 362 + struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev); 363 + 364 + mutex_lock(&list_mutex); 365 + list_del(&priv->list); 366 + mutex_unlock(&list_mutex); 367 + } 368 + 369 + static const struct wmi_device_id dell_wmi_privacy_wmi_id_table[] = { 370 + { .guid_string = DELL_PRIVACY_GUID }, 371 + { }, 372 + }; 373 + 374 + static struct wmi_driver dell_privacy_wmi_driver = { 375 + .driver = { 376 + .name = "dell-privacy", 377 + }, 378 + .probe = dell_privacy_wmi_probe, 379 + .remove = dell_privacy_wmi_remove, 380 + .id_table = dell_wmi_privacy_wmi_id_table, 381 + }; 382 + 383 + int dell_privacy_register_driver(void) 384 + { 385 + return wmi_driver_register(&dell_privacy_wmi_driver); 386 + } 387 + 388 + void dell_privacy_unregister_driver(void) 389 + { 390 + wmi_driver_unregister(&dell_privacy_wmi_driver); 391 + }
+36
drivers/platform/x86/dell/dell-wmi-privacy.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Dell privacy notification driver 4 + * 5 + * Copyright (C) 2021 Dell Inc. All Rights Reserved. 6 + */ 7 + 8 + #ifndef _DELL_PRIVACY_WMI_H_ 9 + #define _DELL_PRIVACY_WMI_H_ 10 + 11 + #if IS_ENABLED(CONFIG_DELL_WMI_PRIVACY) 12 + bool dell_privacy_has_mic_mute(void); 13 + bool dell_privacy_process_event(int type, int code, int status); 14 + int dell_privacy_register_driver(void); 15 + void dell_privacy_unregister_driver(void); 16 + #else /* CONFIG_DELL_PRIVACY */ 17 + static inline bool dell_privacy_has_mic_mute(void) 18 + { 19 + return false; 20 + } 21 + 22 + static inline bool dell_privacy_process_event(int type, int code, int status) 23 + { 24 + return false; 25 + } 26 + 27 + static inline int dell_privacy_register_driver(void) 28 + { 29 + return 0; 30 + } 31 + 32 + static inline void dell_privacy_unregister_driver(void) 33 + { 34 + } 35 + #endif /* CONFIG_DELL_PRIVACY */ 36 + #endif
+4 -1
drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h
··· 152 152 return ret ? ret : count; \ 153 153 } 154 154 155 + #define check_property_type(attr, prop, valuetype) \ 156 + (attr##_obj[prop].type != valuetype) 157 + 155 158 union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string); 156 159 int get_instance_count(const char *guid_string); 157 160 void strlcpy_attr(char *dest, char *src); 158 161 159 162 int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, 160 - struct kobject *attr_name_kobj); 163 + struct kobject *attr_name_kobj, u32 enum_property_count); 161 164 int alloc_enum_data(void); 162 165 void exit_enum_attributes(void); 163 166
+34 -5
drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c
··· 132 132 * @enumeration_obj: ACPI object with enumeration data 133 133 * @instance_id: The instance to enumerate 134 134 * @attr_name_kobj: The parent kernel object 135 + * @enum_property_count: Total properties count under enumeration type 135 136 */ 136 137 int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, 137 - struct kobject *attr_name_kobj) 138 + struct kobject *attr_name_kobj, u32 enum_property_count) 138 139 { 139 140 int i, next_obj, value_modifier_count, possible_values_count; 140 141 141 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 145 strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name, 143 146 enumeration_obj[ATTR_NAME].string.pointer); 147 + if (check_property_type(enumeration, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING)) 148 + return -EINVAL; 144 149 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code, 145 150 enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer); 151 + if (check_property_type(enumeration, DISPLAY_NAME, ACPI_TYPE_STRING)) 152 + return -EINVAL; 146 153 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name, 147 154 enumeration_obj[DISPLAY_NAME].string.pointer); 155 + if (check_property_type(enumeration, DEFAULT_VAL, ACPI_TYPE_STRING)) 156 + return -EINVAL; 148 157 strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value, 149 158 enumeration_obj[DEFAULT_VAL].string.pointer); 159 + if (check_property_type(enumeration, MODIFIER, ACPI_TYPE_STRING)) 160 + return -EINVAL; 150 161 strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier, 151 162 enumeration_obj[MODIFIER].string.pointer); 152 163 153 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 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 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 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 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 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 197 strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";"); 169 198 } 170 199
+16
drivers/platform/x86/dell/dell-wmi-sysman/int-attributes.c
··· 141 141 struct kobject *attr_name_kobj) 142 142 { 143 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 146 strlcpy_attr(wmi_priv.integer_data[instance_id].attribute_name, 145 147 integer_obj[ATTR_NAME].string.pointer); 148 + if (check_property_type(integer, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING)) 149 + return -EINVAL; 146 150 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name_language_code, 147 151 integer_obj[DISPL_NAME_LANG_CODE].string.pointer); 152 + if (check_property_type(integer, DISPLAY_NAME, ACPI_TYPE_STRING)) 153 + return -EINVAL; 148 154 strlcpy_attr(wmi_priv.integer_data[instance_id].display_name, 149 155 integer_obj[DISPLAY_NAME].string.pointer); 156 + if (check_property_type(integer, DEFAULT_VAL, ACPI_TYPE_INTEGER)) 157 + return -EINVAL; 150 158 wmi_priv.integer_data[instance_id].default_value = 151 159 (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; 160 + if (check_property_type(integer, MODIFIER, ACPI_TYPE_STRING)) 161 + return -EINVAL; 152 162 strlcpy_attr(wmi_priv.integer_data[instance_id].dell_modifier, 153 163 integer_obj[MODIFIER].string.pointer); 164 + if (check_property_type(integer, MIN_VALUE, ACPI_TYPE_INTEGER)) 165 + return -EINVAL; 154 166 wmi_priv.integer_data[instance_id].min_value = 155 167 (uintptr_t)integer_obj[MIN_VALUE].string.pointer; 168 + if (check_property_type(integer, MAX_VALUE, ACPI_TYPE_INTEGER)) 169 + return -EINVAL; 156 170 wmi_priv.integer_data[instance_id].max_value = 157 171 (uintptr_t)integer_obj[MAX_VALUE].string.pointer; 172 + if (check_property_type(integer, SCALAR_INCR, ACPI_TYPE_INTEGER)) 173 + return -EINVAL; 158 174 wmi_priv.integer_data[instance_id].scalar_increment = 159 175 (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; 160 176
+6
drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c
··· 159 159 int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj) 160 160 { 161 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 164 strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name, 163 165 po_obj[ATTR_NAME].string.pointer); 166 + if (check_property_type(po, MIN_PASS_LEN, ACPI_TYPE_INTEGER)) 167 + return -EINVAL; 164 168 wmi_priv.po_data[instance_id].min_password_length = 165 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 172 wmi_priv.po_data[instance_id].max_password_length = 167 173 (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer; 168 174
+2 -2
drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c
··· 95 95 96 96 print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size); 97 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 99 if (!ret) 100 - memset(current_password, 0, MAX_BUFF); 100 + strscpy(current_password, new, MAX_BUFF); 101 101 /* explain to user the detailed failure reason */ 102 102 else if (ret == -EOPNOTSUPP) 103 103 dev_err(&wmi_priv.password_attr_wdev->dev, "admin password must be configured\n");
+15 -1
drivers/platform/x86/dell/dell-wmi-sysman/string-attributes.c
··· 118 118 119 119 /** 120 120 * populate_str_data() - Populate all properties of an instance under string attribute 121 - * @str_obj: ACPI object with integer data 121 + * @str_obj: ACPI object with string data 122 122 * @instance_id: The instance to enumerate 123 123 * @attr_name_kobj: The parent kernel object 124 124 */ 125 125 int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj) 126 126 { 127 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 130 strlcpy_attr(wmi_priv.str_data[instance_id].attribute_name, 129 131 str_obj[ATTR_NAME].string.pointer); 132 + if (check_property_type(str, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING)) 133 + return -EINVAL; 130 134 strlcpy_attr(wmi_priv.str_data[instance_id].display_name_language_code, 131 135 str_obj[DISPL_NAME_LANG_CODE].string.pointer); 136 + if (check_property_type(str, DISPLAY_NAME, ACPI_TYPE_STRING)) 137 + return -EINVAL; 132 138 strlcpy_attr(wmi_priv.str_data[instance_id].display_name, 133 139 str_obj[DISPLAY_NAME].string.pointer); 140 + if (check_property_type(str, DEFAULT_VAL, ACPI_TYPE_STRING)) 141 + return -EINVAL; 134 142 strlcpy_attr(wmi_priv.str_data[instance_id].default_value, 135 143 str_obj[DEFAULT_VAL].string.pointer); 144 + if (check_property_type(str, MODIFIER, ACPI_TYPE_STRING)) 145 + return -EINVAL; 136 146 strlcpy_attr(wmi_priv.str_data[instance_id].dell_modifier, 137 147 str_obj[MODIFIER].string.pointer); 148 + if (check_property_type(str, MIN_LEN, ACPI_TYPE_INTEGER)) 149 + return -EINVAL; 138 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 153 wmi_priv.str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer; 140 154 141 155 return sysfs_create_group(attr_name_kobj, &str_attr_group);
+10 -11
drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
··· 13 13 #include <linux/kernel.h> 14 14 #include <linux/wmi.h> 15 15 #include "dell-wmi-sysman.h" 16 + #include "../../firmware_attributes_class.h" 16 17 17 18 #define MAX_TYPES 4 18 19 #include <linux/nls.h> 19 - 20 - static struct class firmware_attributes_class = { 21 - .name = "firmware-attributes", 22 - }; 23 20 24 21 struct wmi_sysman_priv wmi_priv = { 25 22 .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex), ··· 25 28 /* reset bios to defaults */ 26 29 static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; 27 30 static int reset_option = -1; 31 + static struct class *fw_attr_class; 28 32 29 33 30 34 /** ··· 479 481 /* enumerate all of this attribute */ 480 482 switch (attr_type) { 481 483 case ENUM: 482 - retval = populate_enum_data(elements, instance_id, attr_name_kobj); 484 + retval = populate_enum_data(elements, instance_id, attr_name_kobj, 485 + obj->package.count); 483 486 break; 484 487 case INT: 485 488 retval = populate_int_data(elements, instance_id, attr_name_kobj); ··· 540 541 goto err_exit_bios_attr_pass_interface; 541 542 } 542 543 543 - ret = class_register(&firmware_attributes_class); 544 + ret = fw_attributes_class_get(&fw_attr_class); 544 545 if (ret) 545 546 goto err_exit_bios_attr_pass_interface; 546 547 547 - wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), 548 + wmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), 548 549 NULL, "%s", DRIVER_NAME); 549 550 if (IS_ERR(wmi_priv.class_dev)) { 550 551 ret = PTR_ERR(wmi_priv.class_dev); ··· 601 602 release_attributes_data(); 602 603 603 604 err_destroy_classdev: 604 - device_destroy(&firmware_attributes_class, MKDEV(0, 0)); 605 + device_destroy(fw_attr_class, MKDEV(0, 0)); 605 606 606 607 err_unregister_class: 607 - class_unregister(&firmware_attributes_class); 608 + fw_attributes_class_put(); 608 609 609 610 err_exit_bios_attr_pass_interface: 610 611 exit_bios_attr_pass_interface(); ··· 618 619 static void __exit sysman_exit(void) 619 620 { 620 621 release_attributes_data(); 621 - device_destroy(&firmware_attributes_class, MKDEV(0, 0)); 622 - class_unregister(&firmware_attributes_class); 622 + device_destroy(fw_attr_class, MKDEV(0, 0)); 623 + fw_attributes_class_put(); 623 624 exit_bios_attr_set_interface(); 624 625 exit_bios_attr_pass_interface(); 625 626 }
+13 -1
drivers/platform/x86/dell/dell-wmi.c drivers/platform/x86/dell/dell-wmi-base.c
··· 27 27 #include <acpi/video.h> 28 28 #include "dell-smbios.h" 29 29 #include "dell-wmi-descriptor.h" 30 + #include "dell-wmi-privacy.h" 30 31 31 32 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 32 33 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); ··· 428 427 429 428 switch (buffer_entry[1]) { 430 429 case 0x0000: /* One key pressed or event occurred */ 431 - case 0x0012: /* Event with extended data occurred */ 432 430 if (len > 2) 433 431 dell_wmi_process_key(wdev, buffer_entry[1], 434 432 buffer_entry[2]); ··· 438 438 for (i = 2; i < len; ++i) 439 439 dell_wmi_process_key(wdev, buffer_entry[1], 440 440 buffer_entry[i]); 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]); 441 448 break; 442 449 default: /* Unknown event */ 443 450 pr_info("Unknown WMI event type 0x%x\n", ··· 754 747 } 755 748 } 756 749 750 + err = dell_privacy_register_driver(); 751 + if (err) 752 + return err; 753 + 757 754 return wmi_driver_register(&dell_wmi_driver); 758 755 } 759 756 late_initcall(dell_wmi_init); ··· 768 757 dell_wmi_events_set_enabled(false); 769 758 770 759 wmi_driver_unregister(&dell_wmi_driver); 760 + dell_privacy_unregister_driver(); 771 761 } 772 762 module_exit(dell_wmi_exit); 773 763
+52
drivers/platform/x86/firmware_attributes_class.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + 3 + /* Firmware attributes class helper module */ 4 + 5 + #include <linux/mutex.h> 6 + #include <linux/device/class.h> 7 + #include <linux/module.h> 8 + #include "firmware_attributes_class.h" 9 + 10 + static DEFINE_MUTEX(fw_attr_lock); 11 + static int fw_attr_inuse; 12 + 13 + static struct class firmware_attributes_class = { 14 + .name = "firmware-attributes", 15 + }; 16 + 17 + int fw_attributes_class_get(struct class **fw_attr_class) 18 + { 19 + int err; 20 + 21 + mutex_lock(&fw_attr_lock); 22 + if (!fw_attr_inuse) { /*first time class is being used*/ 23 + err = class_register(&firmware_attributes_class); 24 + if (err) { 25 + mutex_unlock(&fw_attr_lock); 26 + return err; 27 + } 28 + } 29 + fw_attr_inuse++; 30 + *fw_attr_class = &firmware_attributes_class; 31 + mutex_unlock(&fw_attr_lock); 32 + return 0; 33 + } 34 + EXPORT_SYMBOL_GPL(fw_attributes_class_get); 35 + 36 + int fw_attributes_class_put(void) 37 + { 38 + mutex_lock(&fw_attr_lock); 39 + if (!fw_attr_inuse) { 40 + mutex_unlock(&fw_attr_lock); 41 + return -EINVAL; 42 + } 43 + fw_attr_inuse--; 44 + if (!fw_attr_inuse) /* No more consumers */ 45 + class_unregister(&firmware_attributes_class); 46 + mutex_unlock(&fw_attr_lock); 47 + return 0; 48 + } 49 + EXPORT_SYMBOL_GPL(fw_attributes_class_put); 50 + 51 + MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>"); 52 + MODULE_LICENSE("GPL");
+11
drivers/platform/x86/firmware_attributes_class.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + /* Firmware attributes class helper module */ 4 + 5 + #ifndef FW_ATTR_CLASS_H 6 + #define FW_ATTR_CLASS_H 7 + 8 + int fw_attributes_class_get(struct class **fw_attr_class); 9 + int fw_attributes_class_put(void); 10 + 11 + #endif /* FW_ATTR_CLASS_H */
+1 -1
drivers/platform/x86/hdaps.c
··· 462 462 NULL, 463 463 }; 464 464 465 - static struct attribute_group hdaps_attribute_group = { 465 + static const struct attribute_group hdaps_attribute_group = { 466 466 .attrs = hdaps_attributes, 467 467 }; 468 468
-102
drivers/platform/x86/hp-wireless.c
··· 1 - // SPDX-License-Identifier: GPL-2.0-or-later 2 - /* 3 - * Airplane mode button for HP & Xiaomi laptops 4 - * 5 - * Copyright (C) 2014-2017 Alex Hung <alex.hung@canonical.com> 6 - */ 7 - 8 - #include <linux/kernel.h> 9 - #include <linux/module.h> 10 - #include <linux/init.h> 11 - #include <linux/input.h> 12 - #include <linux/platform_device.h> 13 - #include <linux/acpi.h> 14 - #include <acpi/acpi_bus.h> 15 - 16 - MODULE_LICENSE("GPL"); 17 - MODULE_AUTHOR("Alex Hung"); 18 - MODULE_ALIAS("acpi*:HPQ6001:*"); 19 - MODULE_ALIAS("acpi*:WSTADEF:*"); 20 - MODULE_ALIAS("acpi*:AMDI0051:*"); 21 - 22 - static struct input_dev *hpwl_input_dev; 23 - 24 - static const struct acpi_device_id hpwl_ids[] = { 25 - {"HPQ6001", 0}, 26 - {"WSTADEF", 0}, 27 - {"AMDI0051", 0}, 28 - {"", 0}, 29 - }; 30 - 31 - static int hp_wireless_input_setup(void) 32 - { 33 - int err; 34 - 35 - hpwl_input_dev = input_allocate_device(); 36 - if (!hpwl_input_dev) 37 - return -ENOMEM; 38 - 39 - hpwl_input_dev->name = "HP Wireless hotkeys"; 40 - hpwl_input_dev->phys = "hpq6001/input0"; 41 - hpwl_input_dev->id.bustype = BUS_HOST; 42 - hpwl_input_dev->evbit[0] = BIT(EV_KEY); 43 - set_bit(KEY_RFKILL, hpwl_input_dev->keybit); 44 - 45 - err = input_register_device(hpwl_input_dev); 46 - if (err) 47 - goto err_free_dev; 48 - 49 - return 0; 50 - 51 - err_free_dev: 52 - input_free_device(hpwl_input_dev); 53 - return err; 54 - } 55 - 56 - static void hp_wireless_input_destroy(void) 57 - { 58 - input_unregister_device(hpwl_input_dev); 59 - } 60 - 61 - static void hpwl_notify(struct acpi_device *acpi_dev, u32 event) 62 - { 63 - if (event != 0x80) { 64 - pr_info("Received unknown event (0x%x)\n", event); 65 - return; 66 - } 67 - 68 - input_report_key(hpwl_input_dev, KEY_RFKILL, 1); 69 - input_sync(hpwl_input_dev); 70 - input_report_key(hpwl_input_dev, KEY_RFKILL, 0); 71 - input_sync(hpwl_input_dev); 72 - } 73 - 74 - static int hpwl_add(struct acpi_device *device) 75 - { 76 - int err; 77 - 78 - err = hp_wireless_input_setup(); 79 - if (err) 80 - pr_err("Failed to setup hp wireless hotkeys\n"); 81 - 82 - return err; 83 - } 84 - 85 - static int hpwl_remove(struct acpi_device *device) 86 - { 87 - hp_wireless_input_destroy(); 88 - return 0; 89 - } 90 - 91 - static struct acpi_driver hpwl_driver = { 92 - .name = "hp-wireless", 93 - .owner = THIS_MODULE, 94 - .ids = hpwl_ids, 95 - .ops = { 96 - .add = hpwl_add, 97 - .remove = hpwl_remove, 98 - .notify = hpwl_notify, 99 - }, 100 - }; 101 - 102 - module_acpi_driver(hpwl_driver);
+12
drivers/platform/x86/ideapad-laptop.c
··· 1408 1408 case 6: 1409 1409 ideapad_input_report(priv, bit); 1410 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 1423 case 9: 1412 1424 ideapad_sync_rfk_state(priv); 1413 1425 break;
+22
drivers/platform/x86/intel/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Intel x86 Platform Specific Drivers 4 + # 5 + 6 + menuconfig X86_PLATFORM_DRIVERS_INTEL 7 + bool "Intel x86 Platform Specific Device Drivers" 8 + default y 9 + help 10 + Say Y here to get to see options for device drivers for 11 + various Intel x86 platforms, including vendor-specific 12 + drivers. This option alone does not add any kernel code. 13 + 14 + If you say N, all options in this submenu will be skipped 15 + and disabled. 16 + 17 + if X86_PLATFORM_DRIVERS_INTEL 18 + 19 + source "drivers/platform/x86/intel/int33fe/Kconfig" 20 + source "drivers/platform/x86/intel/int3472/Kconfig" 21 + 22 + endif # X86_PLATFORM_DRIVERS_INTEL
+8
drivers/platform/x86/intel/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for drivers/platform/x86/intel 4 + # Intel x86 Platform-Specific Drivers 5 + # 6 + 7 + obj-$(CONFIG_INTEL_CHT_INT33FE) += int33fe/ 8 + obj-$(CONFIG_INTEL_SKL_INT3472) += int3472/
+24
drivers/platform/x86/intel/int33fe/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + config INTEL_CHT_INT33FE 3 + tristate "Intel Cherry Trail ACPI INT33FE Driver" 4 + depends on X86 && ACPI && I2C && REGULATOR 5 + depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m) 6 + depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m) 7 + depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m) 8 + help 9 + This driver add support for the INT33FE ACPI device found on 10 + some Intel Cherry Trail devices. 11 + 12 + There are two kinds of INT33FE ACPI device possible: for hardware 13 + with USB Type-C and Micro-B connectors. This driver supports both. 14 + 15 + The INT33FE ACPI device has a CRS table with I2cSerialBusV2 16 + resources for Fuel Gauge Controller and (in the Type-C variant) 17 + FUSB302 USB Type-C Controller and PI3USB30532 USB switch. 18 + This driver instantiates i2c-clients for these, so that standard 19 + i2c drivers for these chips can bind to the them. 20 + 21 + If you enable this driver it is advised to also select 22 + CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B 23 + device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m 24 + for Type-C device.
+5
drivers/platform/x86/intel/int33fe/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o 3 + intel_cht_int33fe-objs := intel_cht_int33fe_common.o \ 4 + intel_cht_int33fe_typec.o \ 5 + intel_cht_int33fe_microb.o
+30
drivers/platform/x86/intel/int3472/Kconfig
··· 1 + config INTEL_SKL_INT3472 2 + tristate "Intel SkyLake ACPI INT3472 Driver" 3 + depends on ACPI 4 + depends on COMMON_CLK 5 + depends on I2C 6 + depends on GPIOLIB 7 + depends on REGULATOR 8 + select MFD_CORE 9 + select REGMAP_I2C 10 + help 11 + This driver adds power controller support for the Intel SkyCam 12 + devices found on the Intel SkyLake platforms. 13 + 14 + The INT3472 is a camera power controller, a logical device found on 15 + Intel Skylake-based systems that can map to different hardware 16 + devices depending on the platform. On machines designed for Chrome OS 17 + it maps to a TPS68470 camera PMIC. On machines designed for Windows, 18 + it maps to either a TP68470 camera PMIC, a uP6641Q sensor PMIC, or a 19 + set of discrete GPIOs and power gates. 20 + 21 + If your device was designed for Chrome OS, this driver will provide 22 + an ACPI OpRegion, which must be available before any of the devices 23 + using it are probed. For this reason, you should select Y if your 24 + device was designed for ChromeOS. For the same reason the 25 + I2C_DESIGNWARE_PLATFORM option must be set to Y too. 26 + 27 + Say Y or M here if you have a SkyLake device designed for use 28 + with Windows or ChromeOS. Say N here if you are not sure. 29 + 30 + The module will be named "intel-skl-int3472".
+5
drivers/platform/x86/intel/int3472/Makefile
··· 1 + obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472.o 2 + intel_skl_int3472-objs := intel_skl_int3472_common.o \ 3 + intel_skl_int3472_discrete.o \ 4 + intel_skl_int3472_tps68470.o \ 5 + intel_skl_int3472_clk_and_regulator.o
+207
drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Author: Dan Scally <djrscally@gmail.com> */ 3 + 4 + #include <linux/acpi.h> 5 + #include <linux/clkdev.h> 6 + #include <linux/clk-provider.h> 7 + #include <linux/device.h> 8 + #include <linux/gpio/consumer.h> 9 + #include <linux/regulator/driver.h> 10 + #include <linux/slab.h> 11 + 12 + #include "intel_skl_int3472_common.h" 13 + 14 + /* 15 + * The regulators have to have .ops to be valid, but the only ops we actually 16 + * support are .enable and .disable which are handled via .ena_gpiod. Pass an 17 + * empty struct to clear the check without lying about capabilities. 18 + */ 19 + static const struct regulator_ops int3472_gpio_regulator_ops; 20 + 21 + static int skl_int3472_clk_prepare(struct clk_hw *hw) 22 + { 23 + struct int3472_gpio_clock *clk = to_int3472_clk(hw); 24 + 25 + gpiod_set_value_cansleep(clk->ena_gpio, 1); 26 + gpiod_set_value_cansleep(clk->led_gpio, 1); 27 + 28 + return 0; 29 + } 30 + 31 + static void skl_int3472_clk_unprepare(struct clk_hw *hw) 32 + { 33 + struct int3472_gpio_clock *clk = to_int3472_clk(hw); 34 + 35 + gpiod_set_value_cansleep(clk->ena_gpio, 0); 36 + gpiod_set_value_cansleep(clk->led_gpio, 0); 37 + } 38 + 39 + static int skl_int3472_clk_enable(struct clk_hw *hw) 40 + { 41 + /* 42 + * We're just turning a GPIO on to enable the clock, which operation 43 + * has the potential to sleep. Given .enable() cannot sleep, but 44 + * .prepare() can, we toggle the GPIO in .prepare() instead. Thus, 45 + * nothing to do here. 46 + */ 47 + return 0; 48 + } 49 + 50 + static void skl_int3472_clk_disable(struct clk_hw *hw) 51 + { 52 + /* Likewise, nothing to do here... */ 53 + } 54 + 55 + static unsigned int skl_int3472_get_clk_frequency(struct int3472_discrete_device *int3472) 56 + { 57 + union acpi_object *obj; 58 + unsigned int freq; 59 + 60 + obj = skl_int3472_get_acpi_buffer(int3472->sensor, "SSDB"); 61 + if (IS_ERR(obj)) 62 + return 0; /* report rate as 0 on error */ 63 + 64 + if (obj->buffer.length < CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET + sizeof(u32)) { 65 + dev_err(int3472->dev, "The buffer is too small\n"); 66 + kfree(obj); 67 + return 0; 68 + } 69 + 70 + freq = *(u32 *)(obj->buffer.pointer + CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET); 71 + 72 + kfree(obj); 73 + return freq; 74 + } 75 + 76 + static unsigned long skl_int3472_clk_recalc_rate(struct clk_hw *hw, 77 + unsigned long parent_rate) 78 + { 79 + struct int3472_gpio_clock *clk = to_int3472_clk(hw); 80 + 81 + return clk->frequency; 82 + } 83 + 84 + static const struct clk_ops skl_int3472_clock_ops = { 85 + .prepare = skl_int3472_clk_prepare, 86 + .unprepare = skl_int3472_clk_unprepare, 87 + .enable = skl_int3472_clk_enable, 88 + .disable = skl_int3472_clk_disable, 89 + .recalc_rate = skl_int3472_clk_recalc_rate, 90 + }; 91 + 92 + int skl_int3472_register_clock(struct int3472_discrete_device *int3472) 93 + { 94 + struct clk_init_data init = { 95 + .ops = &skl_int3472_clock_ops, 96 + .flags = CLK_GET_RATE_NOCACHE, 97 + }; 98 + int ret; 99 + 100 + init.name = kasprintf(GFP_KERNEL, "%s-clk", 101 + acpi_dev_name(int3472->adev)); 102 + if (!init.name) 103 + return -ENOMEM; 104 + 105 + int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472); 106 + 107 + int3472->clock.clk_hw.init = &init; 108 + int3472->clock.clk = clk_register(&int3472->adev->dev, 109 + &int3472->clock.clk_hw); 110 + if (IS_ERR(int3472->clock.clk)) { 111 + ret = PTR_ERR(int3472->clock.clk); 112 + goto out_free_init_name; 113 + } 114 + 115 + int3472->clock.cl = clkdev_create(int3472->clock.clk, NULL, 116 + int3472->sensor_name); 117 + if (!int3472->clock.cl) { 118 + ret = -ENOMEM; 119 + goto err_unregister_clk; 120 + } 121 + 122 + kfree(init.name); 123 + return 0; 124 + 125 + err_unregister_clk: 126 + clk_unregister(int3472->clock.clk); 127 + out_free_init_name: 128 + kfree(init.name); 129 + 130 + return ret; 131 + } 132 + 133 + void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472) 134 + { 135 + clkdev_drop(int3472->clock.cl); 136 + clk_unregister(int3472->clock.clk); 137 + } 138 + 139 + int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, 140 + struct acpi_resource_gpio *agpio) 141 + { 142 + const struct int3472_sensor_config *sensor_config; 143 + char *path = agpio->resource_source.string_ptr; 144 + struct regulator_consumer_supply supply_map; 145 + struct regulator_init_data init_data = { }; 146 + struct regulator_config cfg = { }; 147 + int ret; 148 + 149 + sensor_config = int3472->sensor_config; 150 + if (IS_ERR(sensor_config)) { 151 + dev_err(int3472->dev, "No sensor module config\n"); 152 + return PTR_ERR(sensor_config); 153 + } 154 + 155 + if (!sensor_config->supply_map.supply) { 156 + dev_err(int3472->dev, "No supply name defined\n"); 157 + return -ENODEV; 158 + } 159 + 160 + init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; 161 + init_data.num_consumer_supplies = 1; 162 + supply_map = sensor_config->supply_map; 163 + supply_map.dev_name = int3472->sensor_name; 164 + init_data.consumer_supplies = &supply_map; 165 + 166 + snprintf(int3472->regulator.regulator_name, 167 + sizeof(int3472->regulator.regulator_name), "%s-regulator", 168 + acpi_dev_name(int3472->adev)); 169 + snprintf(int3472->regulator.supply_name, 170 + GPIO_REGULATOR_SUPPLY_NAME_LENGTH, "supply-0"); 171 + 172 + int3472->regulator.rdesc = INT3472_REGULATOR( 173 + int3472->regulator.regulator_name, 174 + int3472->regulator.supply_name, 175 + &int3472_gpio_regulator_ops); 176 + 177 + int3472->regulator.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], 178 + "int3472,regulator"); 179 + if (IS_ERR(int3472->regulator.gpio)) { 180 + dev_err(int3472->dev, "Failed to get regulator GPIO line\n"); 181 + return PTR_ERR(int3472->regulator.gpio); 182 + } 183 + 184 + cfg.dev = &int3472->adev->dev; 185 + cfg.init_data = &init_data; 186 + cfg.ena_gpiod = int3472->regulator.gpio; 187 + 188 + int3472->regulator.rdev = regulator_register(&int3472->regulator.rdesc, 189 + &cfg); 190 + if (IS_ERR(int3472->regulator.rdev)) { 191 + ret = PTR_ERR(int3472->regulator.rdev); 192 + goto err_free_gpio; 193 + } 194 + 195 + return 0; 196 + 197 + err_free_gpio: 198 + gpiod_put(int3472->regulator.gpio); 199 + 200 + return ret; 201 + } 202 + 203 + void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472) 204 + { 205 + regulator_unregister(int3472->regulator.rdev); 206 + gpiod_put(int3472->regulator.gpio); 207 + }
+106
drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Author: Dan Scally <djrscally@gmail.com> */ 3 + 4 + #include <linux/acpi.h> 5 + #include <linux/i2c.h> 6 + #include <linux/platform_device.h> 7 + #include <linux/slab.h> 8 + 9 + #include "intel_skl_int3472_common.h" 10 + 11 + union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id) 12 + { 13 + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 14 + acpi_handle handle = adev->handle; 15 + union acpi_object *obj; 16 + acpi_status status; 17 + 18 + status = acpi_evaluate_object(handle, id, NULL, &buffer); 19 + if (ACPI_FAILURE(status)) 20 + return ERR_PTR(-ENODEV); 21 + 22 + obj = buffer.pointer; 23 + if (!obj) 24 + return ERR_PTR(-ENODEV); 25 + 26 + if (obj->type != ACPI_TYPE_BUFFER) { 27 + acpi_handle_err(handle, "%s object is not an ACPI buffer\n", id); 28 + kfree(obj); 29 + return ERR_PTR(-EINVAL); 30 + } 31 + 32 + return obj; 33 + } 34 + 35 + int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) 36 + { 37 + union acpi_object *obj; 38 + int ret; 39 + 40 + obj = skl_int3472_get_acpi_buffer(adev, "CLDB"); 41 + if (IS_ERR(obj)) 42 + return PTR_ERR(obj); 43 + 44 + if (obj->buffer.length > sizeof(*cldb)) { 45 + acpi_handle_err(adev->handle, "The CLDB buffer is too large\n"); 46 + ret = -EINVAL; 47 + goto out_free_obj; 48 + } 49 + 50 + memcpy(cldb, obj->buffer.pointer, obj->buffer.length); 51 + ret = 0; 52 + 53 + out_free_obj: 54 + kfree(obj); 55 + return ret; 56 + } 57 + 58 + static const struct acpi_device_id int3472_device_id[] = { 59 + { "INT3472", 0 }, 60 + { } 61 + }; 62 + MODULE_DEVICE_TABLE(acpi, int3472_device_id); 63 + 64 + static struct platform_driver int3472_discrete = { 65 + .driver = { 66 + .name = "int3472-discrete", 67 + .acpi_match_table = int3472_device_id, 68 + }, 69 + .probe = skl_int3472_discrete_probe, 70 + .remove = skl_int3472_discrete_remove, 71 + }; 72 + 73 + static struct i2c_driver int3472_tps68470 = { 74 + .driver = { 75 + .name = "int3472-tps68470", 76 + .acpi_match_table = int3472_device_id, 77 + }, 78 + .probe_new = skl_int3472_tps68470_probe, 79 + }; 80 + 81 + static int skl_int3472_init(void) 82 + { 83 + int ret; 84 + 85 + ret = platform_driver_register(&int3472_discrete); 86 + if (ret) 87 + return ret; 88 + 89 + ret = i2c_register_driver(THIS_MODULE, &int3472_tps68470); 90 + if (ret) 91 + platform_driver_unregister(&int3472_discrete); 92 + 93 + return ret; 94 + } 95 + module_init(skl_int3472_init); 96 + 97 + static void skl_int3472_exit(void) 98 + { 99 + platform_driver_unregister(&int3472_discrete); 100 + i2c_del_driver(&int3472_tps68470); 101 + } 102 + module_exit(skl_int3472_exit); 103 + 104 + MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver"); 105 + MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>"); 106 + MODULE_LICENSE("GPL v2");
+122
drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Author: Dan Scally <djrscally@gmail.com> */ 3 + 4 + #ifndef _INTEL_SKL_INT3472_H 5 + #define _INTEL_SKL_INT3472_H 6 + 7 + #include <linux/clk-provider.h> 8 + #include <linux/gpio/machine.h> 9 + #include <linux/regulator/driver.h> 10 + #include <linux/regulator/machine.h> 11 + #include <linux/types.h> 12 + 13 + /* FIXME drop this once the I2C_DEV_NAME_FORMAT macro has been added to include/linux/i2c.h */ 14 + #ifndef I2C_DEV_NAME_FORMAT 15 + #define I2C_DEV_NAME_FORMAT "i2c-%s" 16 + #endif 17 + 18 + /* PMIC GPIO Types */ 19 + #define INT3472_GPIO_TYPE_RESET 0x00 20 + #define INT3472_GPIO_TYPE_POWERDOWN 0x01 21 + #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b 22 + #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c 23 + #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d 24 + 25 + #define INT3472_PDEV_MAX_NAME_LEN 23 26 + #define INT3472_MAX_SENSOR_GPIOS 3 27 + 28 + #define GPIO_REGULATOR_NAME_LENGTH 21 29 + #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 30 + 31 + #define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86 32 + 33 + #define INT3472_REGULATOR(_name, _supply, _ops) \ 34 + (const struct regulator_desc) { \ 35 + .name = _name, \ 36 + .supply_name = _supply, \ 37 + .type = REGULATOR_VOLTAGE, \ 38 + .ops = _ops, \ 39 + .owner = THIS_MODULE, \ 40 + } 41 + 42 + #define to_int3472_clk(hw) \ 43 + container_of(hw, struct int3472_gpio_clock, clk_hw) 44 + 45 + #define to_int3472_device(clk) \ 46 + container_of(clk, struct int3472_discrete_device, clock) 47 + 48 + struct acpi_device; 49 + struct i2c_client; 50 + struct platform_device; 51 + 52 + struct int3472_cldb { 53 + u8 version; 54 + /* 55 + * control logic type 56 + * 0: UNKNOWN 57 + * 1: DISCRETE(CRD-D) 58 + * 2: PMIC TPS68470 59 + * 3: PMIC uP6641 60 + */ 61 + u8 control_logic_type; 62 + u8 control_logic_id; 63 + u8 sensor_card_sku; 64 + u8 reserved[28]; 65 + }; 66 + 67 + struct int3472_gpio_function_remap { 68 + const char *documented; 69 + const char *actual; 70 + }; 71 + 72 + struct int3472_sensor_config { 73 + const char *sensor_module_name; 74 + struct regulator_consumer_supply supply_map; 75 + const struct int3472_gpio_function_remap *function_maps; 76 + }; 77 + 78 + struct int3472_discrete_device { 79 + struct acpi_device *adev; 80 + struct device *dev; 81 + struct acpi_device *sensor; 82 + const char *sensor_name; 83 + 84 + const struct int3472_sensor_config *sensor_config; 85 + 86 + struct int3472_gpio_regulator { 87 + char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; 88 + char supply_name[GPIO_REGULATOR_SUPPLY_NAME_LENGTH]; 89 + struct gpio_desc *gpio; 90 + struct regulator_dev *rdev; 91 + struct regulator_desc rdesc; 92 + } regulator; 93 + 94 + struct int3472_gpio_clock { 95 + struct clk *clk; 96 + struct clk_hw clk_hw; 97 + struct clk_lookup *cl; 98 + struct gpio_desc *ena_gpio; 99 + struct gpio_desc *led_gpio; 100 + u32 frequency; 101 + } clock; 102 + 103 + unsigned int ngpios; /* how many GPIOs have we seen */ 104 + unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ 105 + struct gpiod_lookup_table gpios; 106 + }; 107 + 108 + int skl_int3472_discrete_probe(struct platform_device *pdev); 109 + int skl_int3472_discrete_remove(struct platform_device *pdev); 110 + int skl_int3472_tps68470_probe(struct i2c_client *client); 111 + union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, 112 + char *id); 113 + int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb); 114 + 115 + int skl_int3472_register_clock(struct int3472_discrete_device *int3472); 116 + void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); 117 + 118 + int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, 119 + struct acpi_resource_gpio *agpio); 120 + void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); 121 + 122 + #endif
+413
drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Author: Dan Scally <djrscally@gmail.com> */ 3 + 4 + #include <linux/acpi.h> 5 + #include <linux/clkdev.h> 6 + #include <linux/clk-provider.h> 7 + #include <linux/device.h> 8 + #include <linux/gpio/consumer.h> 9 + #include <linux/gpio/machine.h> 10 + #include <linux/i2c.h> 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/overflow.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/uuid.h> 16 + 17 + #include "intel_skl_int3472_common.h" 18 + 19 + /* 20 + * 79234640-9e10-4fea-a5c1-b5aa8b19756f 21 + * This _DSM GUID returns information about the GPIO lines mapped to a 22 + * discrete INT3472 device. Function number 1 returns a count of the GPIO 23 + * lines that are mapped. Subsequent functions return 32 bit ints encoding 24 + * information about the GPIO line, including its purpose. 25 + */ 26 + static const guid_t int3472_gpio_guid = 27 + GUID_INIT(0x79234640, 0x9e10, 0x4fea, 28 + 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f); 29 + 30 + /* 31 + * 822ace8f-2814-4174-a56b-5f029fe079ee 32 + * This _DSM GUID returns a string from the sensor device, which acts as a 33 + * module identifier. 34 + */ 35 + static const guid_t cio2_sensor_module_guid = 36 + GUID_INIT(0x822ace8f, 0x2814, 0x4174, 37 + 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee); 38 + 39 + /* 40 + * Here follows platform specific mapping information that we can pass to 41 + * the functions mapping resources to the sensors. Where the sensors have 42 + * a power enable pin defined in DSDT we need to provide a supply name so 43 + * the sensor drivers can find the regulator. The device name will be derived 44 + * from the sensor's ACPI device within the code. Optionally, we can provide a 45 + * NULL terminated array of function name mappings to deal with any platform 46 + * specific deviations from the documented behaviour of GPIOs. 47 + * 48 + * Map a GPIO function name to NULL to prevent the driver from mapping that 49 + * GPIO at all. 50 + */ 51 + 52 + static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = { 53 + { "reset", NULL }, 54 + { "powerdown", "reset" }, 55 + { } 56 + }; 57 + 58 + static const struct int3472_sensor_config int3472_sensor_configs[] = { 59 + /* Lenovo Miix 510-12ISK - OV2680, Front */ 60 + { "GNDF140809R", { 0 }, ov2680_gpio_function_remaps }, 61 + /* Lenovo Miix 510-12ISK - OV5648, Rear */ 62 + { "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL }, 63 + /* Surface Go 1&2 - OV5693, Front */ 64 + { "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL }, 65 + }; 66 + 67 + static const struct int3472_sensor_config * 68 + skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472) 69 + { 70 + union acpi_object *obj; 71 + unsigned int i; 72 + 73 + obj = acpi_evaluate_dsm_typed(int3472->sensor->handle, 74 + &cio2_sensor_module_guid, 0x00, 75 + 0x01, NULL, ACPI_TYPE_STRING); 76 + 77 + if (!obj) { 78 + dev_err(int3472->dev, 79 + "Failed to get sensor module string from _DSM\n"); 80 + return ERR_PTR(-ENODEV); 81 + } 82 + 83 + if (obj->string.type != ACPI_TYPE_STRING) { 84 + dev_err(int3472->dev, 85 + "Sensor _DSM returned a non-string value\n"); 86 + 87 + ACPI_FREE(obj); 88 + return ERR_PTR(-EINVAL); 89 + } 90 + 91 + for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) { 92 + if (!strcmp(int3472_sensor_configs[i].sensor_module_name, 93 + obj->string.pointer)) 94 + break; 95 + } 96 + 97 + ACPI_FREE(obj); 98 + 99 + if (i >= ARRAY_SIZE(int3472_sensor_configs)) 100 + return ERR_PTR(-EINVAL); 101 + 102 + return &int3472_sensor_configs[i]; 103 + } 104 + 105 + static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472, 106 + struct acpi_resource_gpio *agpio, 107 + const char *func, u32 polarity) 108 + { 109 + const struct int3472_sensor_config *sensor_config; 110 + char *path = agpio->resource_source.string_ptr; 111 + struct gpiod_lookup *table_entry; 112 + struct acpi_device *adev; 113 + acpi_handle handle; 114 + acpi_status status; 115 + int ret; 116 + 117 + if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) { 118 + dev_warn(int3472->dev, "Too many GPIOs mapped\n"); 119 + return -EINVAL; 120 + } 121 + 122 + sensor_config = int3472->sensor_config; 123 + if (!IS_ERR(sensor_config) && sensor_config->function_maps) { 124 + const struct int3472_gpio_function_remap *remap; 125 + 126 + for (remap = sensor_config->function_maps; remap->documented; remap++) { 127 + if (!strcmp(func, remap->documented)) { 128 + func = remap->actual; 129 + break; 130 + } 131 + } 132 + } 133 + 134 + /* Functions mapped to NULL should not be mapped to the sensor */ 135 + if (!func) 136 + return 0; 137 + 138 + status = acpi_get_handle(NULL, path, &handle); 139 + if (ACPI_FAILURE(status)) 140 + return -EINVAL; 141 + 142 + ret = acpi_bus_get_device(handle, &adev); 143 + if (ret) 144 + return -ENODEV; 145 + 146 + table_entry = &int3472->gpios.table[int3472->n_sensor_gpios]; 147 + table_entry->key = acpi_dev_name(adev); 148 + table_entry->chip_hwnum = agpio->pin_table[0]; 149 + table_entry->con_id = func; 150 + table_entry->idx = 0; 151 + table_entry->flags = polarity; 152 + 153 + int3472->n_sensor_gpios++; 154 + 155 + return 0; 156 + } 157 + 158 + static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472, 159 + struct acpi_resource_gpio *agpio, u8 type) 160 + { 161 + char *path = agpio->resource_source.string_ptr; 162 + u16 pin = agpio->pin_table[0]; 163 + struct gpio_desc *gpio; 164 + 165 + switch (type) { 166 + case INT3472_GPIO_TYPE_CLK_ENABLE: 167 + gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable"); 168 + if (IS_ERR(gpio)) 169 + return (PTR_ERR(gpio)); 170 + 171 + int3472->clock.ena_gpio = gpio; 172 + break; 173 + case INT3472_GPIO_TYPE_PRIVACY_LED: 174 + gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led"); 175 + if (IS_ERR(gpio)) 176 + return (PTR_ERR(gpio)); 177 + 178 + int3472->clock.led_gpio = gpio; 179 + break; 180 + default: 181 + dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type); 182 + break; 183 + } 184 + 185 + return 0; 186 + } 187 + 188 + /** 189 + * skl_int3472_handle_gpio_resources: Map PMIC resources to consuming sensor 190 + * @ares: A pointer to a &struct acpi_resource 191 + * @data: A pointer to a &struct int3472_discrete_device 192 + * 193 + * This function handles GPIO resources that are against an INT3472 194 + * ACPI device, by checking the value of the corresponding _DSM entry. 195 + * This will return a 32bit int, where the lowest byte represents the 196 + * function of the GPIO pin: 197 + * 198 + * 0x00 Reset 199 + * 0x01 Power down 200 + * 0x0b Power enable 201 + * 0x0c Clock enable 202 + * 0x0d Privacy LED 203 + * 204 + * There are some known platform specific quirks where that does not quite 205 + * hold up; for example where a pin with type 0x01 (Power down) is mapped to 206 + * a sensor pin that performs a reset function or entries in _CRS and _DSM that 207 + * do not actually correspond to a physical connection. These will be handled 208 + * by the mapping sub-functions. 209 + * 210 + * GPIOs will either be mapped directly to the sensor device or else used 211 + * to create clocks and regulators via the usual frameworks. 212 + * 213 + * Return: 214 + * * 1 - To continue the loop 215 + * * 0 - When all resources found are handled properly. 216 + * * -EINVAL - If the resource is not a GPIO IO resource 217 + * * -ENODEV - If the resource has no corresponding _DSM entry 218 + * * -Other - Errors propagated from one of the sub-functions. 219 + */ 220 + static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, 221 + void *data) 222 + { 223 + struct int3472_discrete_device *int3472 = data; 224 + struct acpi_resource_gpio *agpio; 225 + union acpi_object *obj; 226 + const char *err_msg; 227 + int ret; 228 + u8 type; 229 + 230 + if (!acpi_gpio_get_io_resource(ares, &agpio)) 231 + return 1; 232 + 233 + /* 234 + * ngpios + 2 because the index of this _DSM function is 1-based and 235 + * the first function is just a count. 236 + */ 237 + obj = acpi_evaluate_dsm_typed(int3472->adev->handle, 238 + &int3472_gpio_guid, 0x00, 239 + int3472->ngpios + 2, 240 + NULL, ACPI_TYPE_INTEGER); 241 + 242 + if (!obj) { 243 + dev_warn(int3472->dev, "No _DSM entry for GPIO pin %u\n", 244 + agpio->pin_table[0]); 245 + return 1; 246 + } 247 + 248 + type = obj->integer.value & 0xff; 249 + 250 + switch (type) { 251 + case INT3472_GPIO_TYPE_RESET: 252 + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", 253 + GPIO_ACTIVE_LOW); 254 + if (ret) 255 + err_msg = "Failed to map reset pin to sensor\n"; 256 + 257 + break; 258 + case INT3472_GPIO_TYPE_POWERDOWN: 259 + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", 260 + GPIO_ACTIVE_LOW); 261 + if (ret) 262 + err_msg = "Failed to map powerdown pin to sensor\n"; 263 + 264 + break; 265 + case INT3472_GPIO_TYPE_CLK_ENABLE: 266 + case INT3472_GPIO_TYPE_PRIVACY_LED: 267 + ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); 268 + if (ret) 269 + err_msg = "Failed to map GPIO to clock\n"; 270 + 271 + break; 272 + case INT3472_GPIO_TYPE_POWER_ENABLE: 273 + ret = skl_int3472_register_regulator(int3472, agpio); 274 + if (ret) 275 + err_msg = "Failed to map regulator to sensor\n"; 276 + 277 + break; 278 + default: 279 + dev_warn(int3472->dev, 280 + "GPIO type 0x%02x unknown; the sensor may not work\n", 281 + type); 282 + ret = 1; 283 + break; 284 + } 285 + 286 + int3472->ngpios++; 287 + ACPI_FREE(obj); 288 + 289 + if (ret < 0) 290 + return dev_err_probe(int3472->dev, ret, err_msg); 291 + 292 + return ret; 293 + } 294 + 295 + static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) 296 + { 297 + LIST_HEAD(resource_list); 298 + int ret; 299 + 300 + /* 301 + * No error check, because not having a sensor config is not necessarily 302 + * a failure mode. 303 + */ 304 + int3472->sensor_config = skl_int3472_get_sensor_module_config(int3472); 305 + 306 + ret = acpi_dev_get_resources(int3472->adev, &resource_list, 307 + skl_int3472_handle_gpio_resources, 308 + int3472); 309 + if (ret < 0) 310 + return ret; 311 + 312 + acpi_dev_free_resource_list(&resource_list); 313 + 314 + /* 315 + * If we find no clock enable GPIO pin then the privacy LED won't work. 316 + * We've never seen that situation, but it's possible. Warn the user so 317 + * it's clear what's happened. 318 + */ 319 + if (int3472->clock.ena_gpio) { 320 + ret = skl_int3472_register_clock(int3472); 321 + if (ret) 322 + return ret; 323 + } else { 324 + if (int3472->clock.led_gpio) 325 + dev_warn(int3472->dev, 326 + "No clk GPIO. The privacy LED won't work\n"); 327 + } 328 + 329 + int3472->gpios.dev_id = int3472->sensor_name; 330 + gpiod_add_lookup_table(&int3472->gpios); 331 + 332 + return 0; 333 + } 334 + 335 + int skl_int3472_discrete_probe(struct platform_device *pdev) 336 + { 337 + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); 338 + struct int3472_discrete_device *int3472; 339 + struct int3472_cldb cldb; 340 + int ret; 341 + 342 + ret = skl_int3472_fill_cldb(adev, &cldb); 343 + if (ret) { 344 + dev_err(&pdev->dev, "Couldn't fill CLDB structure\n"); 345 + return ret; 346 + } 347 + 348 + if (cldb.control_logic_type != 1) { 349 + dev_err(&pdev->dev, "Unsupported control logic type %u\n", 350 + cldb.control_logic_type); 351 + return -EINVAL; 352 + } 353 + 354 + /* Max num GPIOs we've seen plus a terminator */ 355 + int3472 = devm_kzalloc(&pdev->dev, struct_size(int3472, gpios.table, 356 + INT3472_MAX_SENSOR_GPIOS + 1), GFP_KERNEL); 357 + if (!int3472) 358 + return -ENOMEM; 359 + 360 + int3472->adev = adev; 361 + int3472->dev = &pdev->dev; 362 + platform_set_drvdata(pdev, int3472); 363 + 364 + int3472->sensor = acpi_dev_get_first_consumer_dev(adev); 365 + if (!int3472->sensor) { 366 + dev_err(&pdev->dev, "INT3472 seems to have no dependents.\n"); 367 + return -ENODEV; 368 + } 369 + 370 + int3472->sensor_name = devm_kasprintf(int3472->dev, GFP_KERNEL, 371 + I2C_DEV_NAME_FORMAT, 372 + acpi_dev_name(int3472->sensor)); 373 + if (!int3472->sensor_name) { 374 + ret = -ENOMEM; 375 + goto err_put_sensor; 376 + } 377 + 378 + /* 379 + * Initialising this list means we can call gpiod_remove_lookup_table() 380 + * in failure paths without issue. 381 + */ 382 + INIT_LIST_HEAD(&int3472->gpios.list); 383 + 384 + ret = skl_int3472_parse_crs(int3472); 385 + if (ret) { 386 + skl_int3472_discrete_remove(pdev); 387 + return ret; 388 + } 389 + 390 + return 0; 391 + 392 + err_put_sensor: 393 + acpi_dev_put(int3472->sensor); 394 + 395 + return ret; 396 + } 397 + 398 + int skl_int3472_discrete_remove(struct platform_device *pdev) 399 + { 400 + struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); 401 + 402 + gpiod_remove_lookup_table(&int3472->gpios); 403 + 404 + if (int3472->clock.ena_gpio) 405 + skl_int3472_unregister_clock(int3472); 406 + 407 + gpiod_put(int3472->clock.ena_gpio); 408 + gpiod_put(int3472->clock.led_gpio); 409 + 410 + skl_int3472_unregister_regulator(int3472); 411 + 412 + return 0; 413 + }
+137
drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Author: Dan Scally <djrscally@gmail.com> */ 3 + 4 + #include <linux/i2c.h> 5 + #include <linux/mfd/core.h> 6 + #include <linux/mfd/tps68470.h> 7 + #include <linux/platform_device.h> 8 + #include <linux/regmap.h> 9 + 10 + #include "intel_skl_int3472_common.h" 11 + 12 + #define DESIGNED_FOR_CHROMEOS 1 13 + #define DESIGNED_FOR_WINDOWS 2 14 + 15 + static const struct mfd_cell tps68470_cros[] = { 16 + { .name = "tps68470-gpio" }, 17 + { .name = "tps68470_pmic_opregion" }, 18 + }; 19 + 20 + static const struct mfd_cell tps68470_win[] = { 21 + { .name = "tps68470-gpio" }, 22 + { .name = "tps68470-clk" }, 23 + { .name = "tps68470-regulator" }, 24 + }; 25 + 26 + static const struct regmap_config tps68470_regmap_config = { 27 + .reg_bits = 8, 28 + .val_bits = 8, 29 + .max_register = TPS68470_REG_MAX, 30 + }; 31 + 32 + static int tps68470_chip_init(struct device *dev, struct regmap *regmap) 33 + { 34 + unsigned int version; 35 + int ret; 36 + 37 + /* Force software reset */ 38 + ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK); 39 + if (ret) 40 + return ret; 41 + 42 + ret = regmap_read(regmap, TPS68470_REG_REVID, &version); 43 + if (ret) { 44 + dev_err(dev, "Failed to read revision register: %d\n", ret); 45 + return ret; 46 + } 47 + 48 + dev_info(dev, "TPS68470 REVID: 0x%02x\n", version); 49 + 50 + return 0; 51 + } 52 + 53 + /** skl_int3472_tps68470_calc_type: Check what platform a device is designed for 54 + * @adev: A pointer to a &struct acpi_device 55 + * 56 + * Check CLDB buffer against the PMIC's adev. If present, then we check 57 + * the value of control_logic_type field and follow one of the 58 + * following scenarios: 59 + * 60 + * 1. No CLDB - likely ACPI tables designed for ChromeOS. We 61 + * create platform devices for the GPIOs and OpRegion drivers. 62 + * 63 + * 2. CLDB, with control_logic_type = 2 - probably ACPI tables 64 + * made for Windows 2-in-1 platforms. Register pdevs for GPIO, 65 + * Clock and Regulator drivers to bind to. 66 + * 67 + * 3. Any other value in control_logic_type, we should never have 68 + * gotten to this point; fail probe and return. 69 + * 70 + * Return: 71 + * * 1 Device intended for ChromeOS 72 + * * 2 Device intended for Windows 73 + * * -EINVAL Where @adev has an object named CLDB but it does not conform to 74 + * our expectations 75 + */ 76 + static int skl_int3472_tps68470_calc_type(struct acpi_device *adev) 77 + { 78 + struct int3472_cldb cldb = { 0 }; 79 + int ret; 80 + 81 + /* 82 + * A CLDB buffer that exists, but which does not match our expectations 83 + * should trigger an error so we don't blindly continue. 84 + */ 85 + ret = skl_int3472_fill_cldb(adev, &cldb); 86 + if (ret && ret != -ENODEV) 87 + return ret; 88 + 89 + if (ret) 90 + return DESIGNED_FOR_CHROMEOS; 91 + 92 + if (cldb.control_logic_type != 2) 93 + return -EINVAL; 94 + 95 + return DESIGNED_FOR_WINDOWS; 96 + } 97 + 98 + int skl_int3472_tps68470_probe(struct i2c_client *client) 99 + { 100 + struct acpi_device *adev = ACPI_COMPANION(&client->dev); 101 + struct regmap *regmap; 102 + int device_type; 103 + int ret; 104 + 105 + regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config); 106 + if (IS_ERR(regmap)) { 107 + dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap)); 108 + return PTR_ERR(regmap); 109 + } 110 + 111 + i2c_set_clientdata(client, regmap); 112 + 113 + ret = tps68470_chip_init(&client->dev, regmap); 114 + if (ret < 0) { 115 + dev_err(&client->dev, "TPS68470 init error %d\n", ret); 116 + return ret; 117 + } 118 + 119 + device_type = skl_int3472_tps68470_calc_type(adev); 120 + switch (device_type) { 121 + case DESIGNED_FOR_WINDOWS: 122 + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, 123 + tps68470_win, ARRAY_SIZE(tps68470_win), 124 + NULL, 0, NULL); 125 + break; 126 + case DESIGNED_FOR_CHROMEOS: 127 + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, 128 + tps68470_cros, ARRAY_SIZE(tps68470_cros), 129 + NULL, 0, NULL); 130 + break; 131 + default: 132 + dev_err(&client->dev, "Failed to add MFD devices\n"); 133 + return device_type; 134 + } 135 + 136 + return ret; 137 + }
drivers/platform/x86/intel_cht_int33fe_common.c drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.c
drivers/platform/x86/intel_cht_int33fe_common.h drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.h
drivers/platform/x86/intel_cht_int33fe_microb.c drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.c
+2 -2
drivers/platform/x86/intel_cht_int33fe_typec.c drivers/platform/x86/intel/int33fe/intel_cht_int33fe_typec.c
··· 168 168 return -ENODEV; 169 169 } 170 170 171 - /* Then the DP child device node */ 172 - data->dp = device_get_named_child_node(&pdev->dev, "DD02"); 171 + /* Then the DP-2 child device node */ 172 + data->dp = device_get_named_child_node(&pdev->dev, "DD04"); 173 173 pci_dev_put(pdev); 174 174 if (!data->dp) 175 175 return -ENODEV;
+1 -1
drivers/platform/x86/intel_ips.c
··· 829 829 830 830 static u16 read_mgtv(struct ips_driver *ips) 831 831 { 832 - u16 ret; 832 + u16 __maybe_unused ret; 833 833 u64 slope, offset; 834 834 u64 val; 835 835
+1 -1
drivers/platform/x86/intel_pmt_crashlog.c
··· 218 218 NULL 219 219 }; 220 220 221 - static struct attribute_group pmt_crashlog_group = { 221 + static const struct attribute_group pmt_crashlog_group = { 222 222 .attrs = pmt_crashlog_attrs, 223 223 }; 224 224
+68 -5
drivers/platform/x86/intel_speed_select_if/isst_if_common.c
··· 281 281 struct isst_if_cpu_info { 282 282 /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */ 283 283 int bus_info[2]; 284 + struct pci_dev *pci_dev[2]; 284 285 int punit_cpu_id; 286 + int numa_node; 285 287 }; 286 288 287 289 static struct isst_if_cpu_info *isst_cpu_info; 290 + #define ISST_MAX_PCI_DOMAINS 8 291 + 292 + static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn) 293 + { 294 + struct pci_dev *matched_pci_dev = NULL; 295 + struct pci_dev *pci_dev = NULL; 296 + int no_matches = 0; 297 + int i, bus_number; 298 + 299 + if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids || 300 + cpu >= num_possible_cpus()) 301 + return NULL; 302 + 303 + bus_number = isst_cpu_info[cpu].bus_info[bus_no]; 304 + if (bus_number < 0) 305 + return NULL; 306 + 307 + for (i = 0; i < ISST_MAX_PCI_DOMAINS; ++i) { 308 + struct pci_dev *_pci_dev; 309 + int node; 310 + 311 + _pci_dev = pci_get_domain_bus_and_slot(i, bus_number, PCI_DEVFN(dev, fn)); 312 + if (!_pci_dev) 313 + continue; 314 + 315 + ++no_matches; 316 + if (!matched_pci_dev) 317 + matched_pci_dev = _pci_dev; 318 + 319 + node = dev_to_node(&_pci_dev->dev); 320 + if (node == NUMA_NO_NODE) { 321 + pr_info("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n", 322 + cpu, bus_no, dev, fn); 323 + continue; 324 + } 325 + 326 + if (node == isst_cpu_info[cpu].numa_node) { 327 + pci_dev = _pci_dev; 328 + break; 329 + } 330 + } 331 + 332 + /* 333 + * If there is no numa matched pci_dev, then there can be following cases: 334 + * 1. CONFIG_NUMA is not defined: In this case if there is only single device 335 + * match, then we don't need numa information. Simply return last match. 336 + * Othewise return NULL. 337 + * 2. NUMA information is not exposed via _SEG method. In this case it is similar 338 + * to case 1. 339 + * 3. Numa information doesn't match with CPU numa node and more than one match 340 + * return NULL. 341 + */ 342 + if (!pci_dev && no_matches == 1) 343 + pci_dev = matched_pci_dev; 344 + 345 + return pci_dev; 346 + } 288 347 289 348 /** 290 349 * isst_if_get_pci_dev() - Get the PCI device instance for a CPU ··· 359 300 */ 360 301 struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn) 361 302 { 362 - int bus_number; 303 + struct pci_dev *pci_dev; 363 304 364 305 if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids || 365 306 cpu >= num_possible_cpus()) 366 307 return NULL; 367 308 368 - bus_number = isst_cpu_info[cpu].bus_info[bus_no]; 369 - if (bus_number < 0) 370 - return NULL; 309 + pci_dev = isst_cpu_info[cpu].pci_dev[bus_no]; 371 310 372 - return pci_get_domain_bus_and_slot(0, bus_number, PCI_DEVFN(dev, fn)); 311 + if (pci_dev && pci_dev->devfn == PCI_DEVFN(dev, fn)) 312 + return pci_dev; 313 + 314 + return _isst_if_get_pci_dev(cpu, bus_no, dev, fn); 373 315 } 374 316 EXPORT_SYMBOL_GPL(isst_if_get_pci_dev); 375 317 ··· 387 327 } else { 388 328 isst_cpu_info[cpu].bus_info[0] = data & 0xff; 389 329 isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff; 330 + isst_cpu_info[cpu].pci_dev[0] = _isst_if_get_pci_dev(cpu, 0, 0, 1); 331 + isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1); 390 332 } 391 333 392 334 ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data); ··· 397 335 return ret; 398 336 } 399 337 isst_cpu_info[cpu].punit_cpu_id = data; 338 + isst_cpu_info[cpu].numa_node = cpu_to_node(cpu); 400 339 401 340 isst_restore_msr_local(cpu); 402 341
+14 -21
drivers/platform/x86/samsung-laptop.c
··· 388 388 "Disable the DMI check and forces the driver to be loaded"); 389 389 390 390 static bool debug; 391 - module_param(debug, bool, S_IRUGO | S_IWUSR); 391 + module_param(debug, bool, 0644); 392 392 MODULE_PARM_DESC(debug, "Debug enabled or not"); 393 393 394 394 static int sabi_command(struct samsung_laptop *samsung, u16 command, ··· 705 705 return count; 706 706 } 707 707 708 - static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, 708 + static DEVICE_ATTR(performance_level, 0644, 709 709 get_performance_level, set_performance_level); 710 710 711 711 static int read_battery_life_extender(struct samsung_laptop *samsung) ··· 774 774 return count; 775 775 } 776 776 777 - static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, 777 + static DEVICE_ATTR(battery_life_extender, 0644, 778 778 get_battery_life_extender, set_battery_life_extender); 779 779 780 780 static int read_usb_charge(struct samsung_laptop *samsung) ··· 843 843 return count; 844 844 } 845 845 846 - static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 846 + static DEVICE_ATTR(usb_charge, 0644, 847 847 get_usb_charge, set_usb_charge); 848 848 849 849 static int read_lid_handling(struct samsung_laptop *samsung) ··· 908 908 return count; 909 909 } 910 910 911 - static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO, 911 + static DEVICE_ATTR(lid_handling, 0644, 912 912 get_lid_handling, set_lid_handling); 913 913 914 914 static struct attribute *platform_attributes[] = { ··· 1291 1291 samsung->debug.sdiag_wrapper.data = samsung->sdiag; 1292 1292 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 1293 1293 1294 - debugfs_create_u16("command", S_IRUGO | S_IWUSR, root, 1295 - &samsung->debug.command); 1296 - debugfs_create_u32("d0", S_IRUGO | S_IWUSR, root, 1297 - &samsung->debug.data.d0); 1298 - debugfs_create_u32("d1", S_IRUGO | S_IWUSR, root, 1299 - &samsung->debug.data.d1); 1300 - debugfs_create_u16("d2", S_IRUGO | S_IWUSR, root, 1301 - &samsung->debug.data.d2); 1302 - debugfs_create_u8("d3", S_IRUGO | S_IWUSR, root, 1303 - &samsung->debug.data.d3); 1304 - debugfs_create_blob("data", S_IRUGO | S_IWUSR, root, 1305 - &samsung->debug.data_wrapper); 1306 - debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, root, 1294 + debugfs_create_u16("command", 0644, root, &samsung->debug.command); 1295 + debugfs_create_u32("d0", 0644, root, &samsung->debug.data.d0); 1296 + debugfs_create_u32("d1", 0644, root, &samsung->debug.data.d1); 1297 + debugfs_create_u16("d2", 0644, root, &samsung->debug.data.d2); 1298 + debugfs_create_u8("d3", 0644, root, &samsung->debug.data.d3); 1299 + debugfs_create_blob("data", 0444, root, &samsung->debug.data_wrapper); 1300 + debugfs_create_blob("f0000_segment", 0400, root, 1307 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 1303 &samsung_laptop_call_fops); 1310 - debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, root, 1311 - &samsung->debug.sdiag_wrapper); 1304 + debugfs_create_blob("sdiag", 0444, root, &samsung->debug.sdiag_wrapper); 1312 1305 } 1313 1306 1314 1307 static void samsung_sabi_exit(struct samsung_laptop *samsung)
+1 -1
drivers/platform/x86/tc1100-wmi.c
··· 156 156 NULL 157 157 }; 158 158 159 - static struct attribute_group tc1100_attribute_group = { 159 + static const struct attribute_group tc1100_attribute_group = { 160 160 .attrs = tc1100_attributes, 161 161 }; 162 162
+904
drivers/platform/x86/think-lmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Think LMI BIOS configuration driver 4 + * 5 + * Copyright(C) 2019-2021 Lenovo 6 + * 7 + * Original code from Thinkpad-wmi project https://github.com/iksaif/thinkpad-wmi 8 + * Copyright(C) 2017 Corentin Chary <corentin.chary@gmail.com> 9 + * Distributed under the GPL-2.0 license 10 + */ 11 + 12 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 + 14 + #include <linux/acpi.h> 15 + #include <linux/errno.h> 16 + #include <linux/fs.h> 17 + #include <linux/string.h> 18 + #include <linux/types.h> 19 + #include <linux/wmi.h> 20 + #include "firmware_attributes_class.h" 21 + #include "think-lmi.h" 22 + 23 + /* 24 + * Name: 25 + * Lenovo_BiosSetting 26 + * Description: 27 + * Get item name and settings for current LMI instance. 28 + * Type: 29 + * Query 30 + * Returns: 31 + * "Item,Value" 32 + * Example: 33 + * "WakeOnLAN,Enable" 34 + */ 35 + #define LENOVO_BIOS_SETTING_GUID "51F5230E-9677-46CD-A1CF-C0B23EE34DB7" 36 + 37 + /* 38 + * Name: 39 + * Lenovo_SetBiosSetting 40 + * Description: 41 + * Change the BIOS setting to the desired value using the Lenovo_SetBiosSetting 42 + * class. To save the settings, use the Lenovo_SaveBiosSetting class. 43 + * BIOS settings and values are case sensitive. 44 + * After making changes to the BIOS settings, you must reboot the computer 45 + * before the changes will take effect. 46 + * Type: 47 + * Method 48 + * Arguments: 49 + * "Item,Value,Password,Encoding,KbdLang;" 50 + * Example: 51 + * "WakeOnLAN,Disable,pa55w0rd,ascii,us;" 52 + */ 53 + #define LENOVO_SET_BIOS_SETTINGS_GUID "98479A64-33F5-4E33-A707-8E251EBBC3A1" 54 + 55 + /* 56 + * Name: 57 + * Lenovo_SaveBiosSettings 58 + * Description: 59 + * Save any pending changes in settings. 60 + * Type: 61 + * Method 62 + * Arguments: 63 + * "Password,Encoding,KbdLang;" 64 + * Example: 65 + * "pa55w0rd,ascii,us;" 66 + */ 67 + #define LENOVO_SAVE_BIOS_SETTINGS_GUID "6A4B54EF-A5ED-4D33-9455-B0D9B48DF4B3" 68 + 69 + /* 70 + * Name: 71 + * Lenovo_BiosPasswordSettings 72 + * Description: 73 + * Return BIOS Password settings 74 + * Type: 75 + * Query 76 + * Returns: 77 + * PasswordMode, PasswordState, MinLength, MaxLength, 78 + * SupportedEncoding, SupportedKeyboard 79 + */ 80 + #define LENOVO_BIOS_PASSWORD_SETTINGS_GUID "8ADB159E-1E32-455C-BC93-308A7ED98246" 81 + 82 + /* 83 + * Name: 84 + * Lenovo_SetBiosPassword 85 + * Description: 86 + * Change a specific password. 87 + * - BIOS settings cannot be changed at the same boot as power-on 88 + * passwords (POP) and hard disk passwords (HDP). If you want to change 89 + * BIOS settings and POP or HDP, you must reboot the system after changing 90 + * one of them. 91 + * - A password cannot be set using this method when one does not already 92 + * exist. Passwords can only be updated or cleared. 93 + * Type: 94 + * Method 95 + * Arguments: 96 + * "PasswordType,CurrentPassword,NewPassword,Encoding,KbdLang;" 97 + * Example: 98 + * "pop,pa55w0rd,newpa55w0rd,ascii,us;” 99 + */ 100 + #define LENOVO_SET_BIOS_PASSWORD_GUID "2651D9FD-911C-4B69-B94E-D0DED5963BD7" 101 + 102 + /* 103 + * Name: 104 + * Lenovo_GetBiosSelections 105 + * Description: 106 + * Return a list of valid settings for a given item. 107 + * Type: 108 + * Method 109 + * Arguments: 110 + * "Item" 111 + * Returns: 112 + * "Value1,Value2,Value3,..." 113 + * Example: 114 + * -> "FlashOverLAN" 115 + * <- "Enabled,Disabled" 116 + */ 117 + #define LENOVO_GET_BIOS_SELECTIONS_GUID "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B" 118 + 119 + #define TLMI_POP_PWD (1 << 0) 120 + #define TLMI_PAP_PWD (1 << 1) 121 + #define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj) 122 + #define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj) 123 + 124 + static const struct tlmi_err_codes tlmi_errs[] = { 125 + {"Success", 0}, 126 + {"Not Supported", -EOPNOTSUPP}, 127 + {"Invalid Parameter", -EINVAL}, 128 + {"Access Denied", -EACCES}, 129 + {"System Busy", -EBUSY}, 130 + }; 131 + 132 + static const char * const encoding_options[] = { 133 + [TLMI_ENCODING_ASCII] = "ascii", 134 + [TLMI_ENCODING_SCANCODE] = "scancode", 135 + }; 136 + static struct think_lmi tlmi_priv; 137 + static struct class *fw_attr_class; 138 + 139 + /* ------ Utility functions ------------*/ 140 + /* Convert BIOS WMI error string to suitable error code */ 141 + static int tlmi_errstr_to_err(const char *errstr) 142 + { 143 + int i; 144 + 145 + for (i = 0; i < sizeof(tlmi_errs)/sizeof(struct tlmi_err_codes); i++) { 146 + if (!strcmp(tlmi_errs[i].err_str, errstr)) 147 + return tlmi_errs[i].err_code; 148 + } 149 + return -EPERM; 150 + } 151 + 152 + /* Extract error string from WMI return buffer */ 153 + static int tlmi_extract_error(const struct acpi_buffer *output) 154 + { 155 + const union acpi_object *obj; 156 + 157 + obj = output->pointer; 158 + if (!obj) 159 + return -ENOMEM; 160 + if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) 161 + return -EIO; 162 + 163 + return tlmi_errstr_to_err(obj->string.pointer); 164 + } 165 + 166 + /* Utility function to execute WMI call to BIOS */ 167 + static int tlmi_simple_call(const char *guid, const char *arg) 168 + { 169 + const struct acpi_buffer input = { strlen(arg), (char *)arg }; 170 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 171 + acpi_status status; 172 + int i, err; 173 + 174 + /* 175 + * Duplicated call required to match BIOS workaround for behavior 176 + * seen when WMI accessed via scripting on other OS. 177 + */ 178 + for (i = 0; i < 2; i++) { 179 + /* (re)initialize output buffer to default state */ 180 + output.length = ACPI_ALLOCATE_BUFFER; 181 + output.pointer = NULL; 182 + 183 + status = wmi_evaluate_method(guid, 0, 0, &input, &output); 184 + if (ACPI_FAILURE(status)) { 185 + kfree(output.pointer); 186 + return -EIO; 187 + } 188 + err = tlmi_extract_error(&output); 189 + kfree(output.pointer); 190 + if (err) 191 + return err; 192 + } 193 + return 0; 194 + } 195 + 196 + /* Extract output string from WMI return buffer */ 197 + static int tlmi_extract_output_string(const struct acpi_buffer *output, 198 + char **string) 199 + { 200 + const union acpi_object *obj; 201 + char *s; 202 + 203 + obj = output->pointer; 204 + if (!obj) 205 + return -ENOMEM; 206 + if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer) 207 + return -EIO; 208 + 209 + s = kstrdup(obj->string.pointer, GFP_KERNEL); 210 + if (!s) 211 + return -ENOMEM; 212 + *string = s; 213 + return 0; 214 + } 215 + 216 + /* ------ Core interface functions ------------*/ 217 + 218 + /* Get password settings from BIOS */ 219 + static int tlmi_get_pwd_settings(struct tlmi_pwdcfg *pwdcfg) 220 + { 221 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 222 + const union acpi_object *obj; 223 + acpi_status status; 224 + 225 + if (!tlmi_priv.can_get_password_settings) 226 + return -EOPNOTSUPP; 227 + 228 + status = wmi_query_block(LENOVO_BIOS_PASSWORD_SETTINGS_GUID, 0, 229 + &output); 230 + if (ACPI_FAILURE(status)) 231 + return -EIO; 232 + 233 + obj = output.pointer; 234 + if (!obj) 235 + return -ENOMEM; 236 + if (obj->type != ACPI_TYPE_BUFFER || !obj->buffer.pointer) { 237 + kfree(obj); 238 + return -EIO; 239 + } 240 + /* 241 + * The size of thinkpad_wmi_pcfg on ThinkStation is larger than ThinkPad. 242 + * To make the driver compatible on different brands, we permit it to get 243 + * the data in below case. 244 + */ 245 + if (obj->buffer.length < sizeof(struct tlmi_pwdcfg)) { 246 + pr_warn("Unknown pwdcfg buffer length %d\n", obj->buffer.length); 247 + kfree(obj); 248 + return -EIO; 249 + } 250 + memcpy(pwdcfg, obj->buffer.pointer, sizeof(struct tlmi_pwdcfg)); 251 + kfree(obj); 252 + return 0; 253 + } 254 + 255 + static int tlmi_save_bios_settings(const char *password) 256 + { 257 + return tlmi_simple_call(LENOVO_SAVE_BIOS_SETTINGS_GUID, 258 + password); 259 + } 260 + 261 + static int tlmi_setting(int item, char **value, const char *guid_string) 262 + { 263 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 264 + acpi_status status; 265 + int ret; 266 + 267 + status = wmi_query_block(guid_string, item, &output); 268 + if (ACPI_FAILURE(status)) { 269 + kfree(output.pointer); 270 + return -EIO; 271 + } 272 + 273 + ret = tlmi_extract_output_string(&output, value); 274 + kfree(output.pointer); 275 + return ret; 276 + } 277 + 278 + static int tlmi_get_bios_selections(const char *item, char **value) 279 + { 280 + const struct acpi_buffer input = { strlen(item), (char *)item }; 281 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 282 + acpi_status status; 283 + int ret; 284 + 285 + status = wmi_evaluate_method(LENOVO_GET_BIOS_SELECTIONS_GUID, 286 + 0, 0, &input, &output); 287 + 288 + if (ACPI_FAILURE(status)) { 289 + kfree(output.pointer); 290 + return -EIO; 291 + } 292 + 293 + ret = tlmi_extract_output_string(&output, value); 294 + kfree(output.pointer); 295 + return ret; 296 + } 297 + 298 + /* ---- Authentication sysfs --------------------------------------------------------- */ 299 + static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, 300 + char *buf) 301 + { 302 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 303 + 304 + return sysfs_emit(buf, "%d\n", setting->valid); 305 + } 306 + 307 + static struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled); 308 + 309 + static ssize_t current_password_store(struct kobject *kobj, 310 + struct kobj_attribute *attr, 311 + const char *buf, size_t count) 312 + { 313 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 314 + size_t pwdlen; 315 + char *p; 316 + 317 + pwdlen = strlen(buf); 318 + /* pwdlen == 0 is allowed to clear the password */ 319 + if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) 320 + return -EINVAL; 321 + 322 + strscpy(setting->password, buf, setting->maxlen); 323 + /* Strip out CR if one is present, setting password won't work if it is present */ 324 + p = strchrnul(setting->password, '\n'); 325 + *p = '\0'; 326 + return count; 327 + } 328 + 329 + static struct kobj_attribute auth_current_password = __ATTR_WO(current_password); 330 + 331 + static ssize_t new_password_store(struct kobject *kobj, 332 + struct kobj_attribute *attr, 333 + const char *buf, size_t count) 334 + { 335 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 336 + char *auth_str, *new_pwd, *p; 337 + size_t pwdlen; 338 + int ret; 339 + 340 + if (!capable(CAP_SYS_ADMIN)) 341 + return -EPERM; 342 + 343 + if (!tlmi_priv.can_set_bios_password) 344 + return -EOPNOTSUPP; 345 + 346 + new_pwd = kstrdup(buf, GFP_KERNEL); 347 + if (!new_pwd) 348 + return -ENOMEM; 349 + 350 + /* Strip out CR if one is present, setting password won't work if it is present */ 351 + p = strchrnul(new_pwd, '\n'); 352 + *p = '\0'; 353 + 354 + pwdlen = strlen(new_pwd); 355 + /* pwdlen == 0 is allowed to clear the password */ 356 + if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) { 357 + ret = -EINVAL; 358 + goto out; 359 + } 360 + 361 + /* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */ 362 + auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;", 363 + setting->pwd_type, setting->password, new_pwd, 364 + encoding_options[setting->encoding], setting->kbdlang); 365 + if (!auth_str) { 366 + ret = -ENOMEM; 367 + goto out; 368 + } 369 + ret = tlmi_simple_call(LENOVO_SET_BIOS_PASSWORD_GUID, auth_str); 370 + kfree(auth_str); 371 + out: 372 + kfree(new_pwd); 373 + return ret ?: count; 374 + } 375 + 376 + static struct kobj_attribute auth_new_password = __ATTR_WO(new_password); 377 + 378 + static ssize_t min_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, 379 + char *buf) 380 + { 381 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 382 + 383 + return sysfs_emit(buf, "%d\n", setting->minlen); 384 + } 385 + 386 + static struct kobj_attribute auth_min_pass_length = __ATTR_RO(min_password_length); 387 + 388 + static ssize_t max_password_length_show(struct kobject *kobj, struct kobj_attribute *attr, 389 + char *buf) 390 + { 391 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 392 + 393 + return sysfs_emit(buf, "%d\n", setting->maxlen); 394 + } 395 + static struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_length); 396 + 397 + static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, 398 + char *buf) 399 + { 400 + return sysfs_emit(buf, "password\n"); 401 + } 402 + static struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism); 403 + 404 + static ssize_t encoding_show(struct kobject *kobj, struct kobj_attribute *attr, 405 + char *buf) 406 + { 407 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 408 + 409 + return sysfs_emit(buf, "%s\n", encoding_options[setting->encoding]); 410 + } 411 + 412 + static ssize_t encoding_store(struct kobject *kobj, 413 + struct kobj_attribute *attr, 414 + const char *buf, size_t count) 415 + { 416 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 417 + int i; 418 + 419 + /* Scan for a matching profile */ 420 + i = sysfs_match_string(encoding_options, buf); 421 + if (i < 0) 422 + return -EINVAL; 423 + 424 + setting->encoding = i; 425 + return count; 426 + } 427 + 428 + static struct kobj_attribute auth_encoding = __ATTR_RW(encoding); 429 + 430 + static ssize_t kbdlang_show(struct kobject *kobj, struct kobj_attribute *attr, 431 + char *buf) 432 + { 433 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 434 + 435 + return sysfs_emit(buf, "%s\n", setting->kbdlang); 436 + } 437 + 438 + static ssize_t kbdlang_store(struct kobject *kobj, 439 + struct kobj_attribute *attr, 440 + const char *buf, size_t count) 441 + { 442 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 443 + int length; 444 + 445 + /* Calculate length till '\n' or terminating 0 */ 446 + length = strchrnul(buf, '\n') - buf; 447 + if (!length || length >= TLMI_LANG_MAXLEN) 448 + return -EINVAL; 449 + 450 + memcpy(setting->kbdlang, buf, length); 451 + setting->kbdlang[length] = '\0'; 452 + return count; 453 + } 454 + 455 + static struct kobj_attribute auth_kbdlang = __ATTR_RW(kbdlang); 456 + 457 + static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, 458 + char *buf) 459 + { 460 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 461 + 462 + return sysfs_emit(buf, "%s\n", setting->role); 463 + } 464 + static struct kobj_attribute auth_role = __ATTR_RO(role); 465 + 466 + static struct attribute *auth_attrs[] = { 467 + &auth_is_pass_set.attr, 468 + &auth_min_pass_length.attr, 469 + &auth_max_pass_length.attr, 470 + &auth_current_password.attr, 471 + &auth_new_password.attr, 472 + &auth_role.attr, 473 + &auth_mechanism.attr, 474 + &auth_encoding.attr, 475 + &auth_kbdlang.attr, 476 + NULL 477 + }; 478 + 479 + static const struct attribute_group auth_attr_group = { 480 + .attrs = auth_attrs, 481 + }; 482 + 483 + /* ---- Attributes sysfs --------------------------------------------------------- */ 484 + static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, 485 + char *buf) 486 + { 487 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 488 + 489 + return sysfs_emit(buf, "%s\n", setting->display_name); 490 + } 491 + 492 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 493 + { 494 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 495 + char *item, *value; 496 + int ret; 497 + 498 + ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID); 499 + if (ret) 500 + return ret; 501 + 502 + /* validate and split from `item,value` -> `value` */ 503 + value = strpbrk(item, ","); 504 + if (!value || value == item || !strlen(value + 1)) 505 + return -EINVAL; 506 + 507 + ret = sysfs_emit(buf, "%s\n", value + 1); 508 + kfree(item); 509 + return ret; 510 + } 511 + 512 + static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 513 + { 514 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 515 + 516 + if (!tlmi_priv.can_get_bios_selections) 517 + return -EOPNOTSUPP; 518 + 519 + return sysfs_emit(buf, "%s\n", setting->possible_values); 520 + } 521 + 522 + static ssize_t current_value_store(struct kobject *kobj, 523 + struct kobj_attribute *attr, 524 + const char *buf, size_t count) 525 + { 526 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 527 + char *set_str = NULL, *new_setting = NULL; 528 + char *auth_str = NULL; 529 + char *p; 530 + int ret; 531 + 532 + if (!tlmi_priv.can_set_bios_settings) 533 + return -EOPNOTSUPP; 534 + 535 + new_setting = kstrdup(buf, GFP_KERNEL); 536 + if (!new_setting) 537 + return -ENOMEM; 538 + 539 + /* Strip out CR if one is present */ 540 + p = strchrnul(new_setting, '\n'); 541 + *p = '\0'; 542 + 543 + if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { 544 + auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", 545 + tlmi_priv.pwd_admin->password, 546 + encoding_options[tlmi_priv.pwd_admin->encoding], 547 + tlmi_priv.pwd_admin->kbdlang); 548 + if (!auth_str) { 549 + ret = -ENOMEM; 550 + goto out; 551 + } 552 + } 553 + 554 + if (auth_str) 555 + set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name, 556 + new_setting, auth_str); 557 + else 558 + set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name, 559 + new_setting); 560 + if (!set_str) { 561 + ret = -ENOMEM; 562 + goto out; 563 + } 564 + 565 + ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str); 566 + if (ret) 567 + goto out; 568 + 569 + if (auth_str) 570 + ret = tlmi_save_bios_settings(auth_str); 571 + else 572 + ret = tlmi_save_bios_settings(""); 573 + 574 + out: 575 + kfree(auth_str); 576 + kfree(set_str); 577 + kfree(new_setting); 578 + return ret ?: count; 579 + } 580 + 581 + static struct kobj_attribute attr_displ_name = __ATTR_RO(display_name); 582 + 583 + static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values); 584 + 585 + static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600); 586 + 587 + static struct attribute *tlmi_attrs[] = { 588 + &attr_displ_name.attr, 589 + &attr_current_val.attr, 590 + &attr_possible_values.attr, 591 + NULL 592 + }; 593 + 594 + static const struct attribute_group tlmi_attr_group = { 595 + .attrs = tlmi_attrs, 596 + }; 597 + 598 + static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr, 599 + char *buf) 600 + { 601 + struct kobj_attribute *kattr; 602 + 603 + kattr = container_of(attr, struct kobj_attribute, attr); 604 + if (kattr->show) 605 + return kattr->show(kobj, kattr, buf); 606 + return -EIO; 607 + } 608 + 609 + static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr, 610 + const char *buf, size_t count) 611 + { 612 + struct kobj_attribute *kattr; 613 + 614 + kattr = container_of(attr, struct kobj_attribute, attr); 615 + if (kattr->store) 616 + return kattr->store(kobj, kattr, buf, count); 617 + return -EIO; 618 + } 619 + 620 + static const struct sysfs_ops tlmi_kobj_sysfs_ops = { 621 + .show = tlmi_attr_show, 622 + .store = tlmi_attr_store, 623 + }; 624 + 625 + static void tlmi_attr_setting_release(struct kobject *kobj) 626 + { 627 + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); 628 + 629 + kfree(setting->possible_values); 630 + kfree(setting); 631 + } 632 + 633 + static void tlmi_pwd_setting_release(struct kobject *kobj) 634 + { 635 + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); 636 + 637 + kfree(setting); 638 + } 639 + 640 + static struct kobj_type tlmi_attr_setting_ktype = { 641 + .release = &tlmi_attr_setting_release, 642 + .sysfs_ops = &tlmi_kobj_sysfs_ops, 643 + }; 644 + 645 + static struct kobj_type tlmi_pwd_setting_ktype = { 646 + .release = &tlmi_pwd_setting_release, 647 + .sysfs_ops = &tlmi_kobj_sysfs_ops, 648 + }; 649 + 650 + /* ---- Initialisation --------------------------------------------------------- */ 651 + static void tlmi_release_attr(void) 652 + { 653 + int i; 654 + 655 + /* Attribute structures */ 656 + for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { 657 + if (tlmi_priv.setting[i]) { 658 + sysfs_remove_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group); 659 + kobject_put(&tlmi_priv.setting[i]->kobj); 660 + } 661 + } 662 + kset_unregister(tlmi_priv.attribute_kset); 663 + 664 + /* Authentication structures */ 665 + sysfs_remove_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group); 666 + kobject_put(&tlmi_priv.pwd_admin->kobj); 667 + sysfs_remove_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 668 + kobject_put(&tlmi_priv.pwd_power->kobj); 669 + kset_unregister(tlmi_priv.authentication_kset); 670 + } 671 + 672 + static int tlmi_sysfs_init(void) 673 + { 674 + int i, ret; 675 + 676 + ret = fw_attributes_class_get(&fw_attr_class); 677 + if (ret) 678 + return ret; 679 + 680 + tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), 681 + NULL, "%s", "thinklmi"); 682 + if (IS_ERR(tlmi_priv.class_dev)) { 683 + ret = PTR_ERR(tlmi_priv.class_dev); 684 + goto fail_class_created; 685 + } 686 + 687 + tlmi_priv.attribute_kset = kset_create_and_add("attributes", NULL, 688 + &tlmi_priv.class_dev->kobj); 689 + if (!tlmi_priv.attribute_kset) { 690 + ret = -ENOMEM; 691 + goto fail_device_created; 692 + } 693 + 694 + for (i = 0; i < TLMI_SETTINGS_COUNT; i++) { 695 + /* Check if index is a valid setting - skip if it isn't */ 696 + if (!tlmi_priv.setting[i]) 697 + continue; 698 + 699 + /* check for duplicate or reserved values */ 700 + if (kset_find_obj(tlmi_priv.attribute_kset, tlmi_priv.setting[i]->display_name) || 701 + !strcmp(tlmi_priv.setting[i]->display_name, "Reserved")) { 702 + pr_debug("duplicate or reserved attribute name found - %s\n", 703 + tlmi_priv.setting[i]->display_name); 704 + kfree(tlmi_priv.setting[i]->possible_values); 705 + kfree(tlmi_priv.setting[i]); 706 + tlmi_priv.setting[i] = NULL; 707 + continue; 708 + } 709 + 710 + /* Build attribute */ 711 + tlmi_priv.setting[i]->kobj.kset = tlmi_priv.attribute_kset; 712 + ret = kobject_init_and_add(&tlmi_priv.setting[i]->kobj, &tlmi_attr_setting_ktype, 713 + NULL, "%s", tlmi_priv.setting[i]->display_name); 714 + if (ret) 715 + goto fail_create_attr; 716 + 717 + ret = sysfs_create_group(&tlmi_priv.setting[i]->kobj, &tlmi_attr_group); 718 + if (ret) 719 + goto fail_create_attr; 720 + } 721 + 722 + /* Create authentication entries */ 723 + tlmi_priv.authentication_kset = kset_create_and_add("authentication", NULL, 724 + &tlmi_priv.class_dev->kobj); 725 + if (!tlmi_priv.authentication_kset) { 726 + ret = -ENOMEM; 727 + goto fail_create_attr; 728 + } 729 + tlmi_priv.pwd_admin->kobj.kset = tlmi_priv.authentication_kset; 730 + ret = kobject_init_and_add(&tlmi_priv.pwd_admin->kobj, &tlmi_pwd_setting_ktype, 731 + NULL, "%s", "Admin"); 732 + if (ret) 733 + goto fail_create_attr; 734 + 735 + ret = sysfs_create_group(&tlmi_priv.pwd_admin->kobj, &auth_attr_group); 736 + if (ret) 737 + goto fail_create_attr; 738 + 739 + tlmi_priv.pwd_power->kobj.kset = tlmi_priv.authentication_kset; 740 + ret = kobject_init_and_add(&tlmi_priv.pwd_power->kobj, &tlmi_pwd_setting_ktype, 741 + NULL, "%s", "System"); 742 + if (ret) 743 + goto fail_create_attr; 744 + 745 + ret = sysfs_create_group(&tlmi_priv.pwd_power->kobj, &auth_attr_group); 746 + if (ret) 747 + goto fail_create_attr; 748 + 749 + return ret; 750 + 751 + fail_create_attr: 752 + tlmi_release_attr(); 753 + fail_device_created: 754 + device_destroy(fw_attr_class, MKDEV(0, 0)); 755 + fail_class_created: 756 + fw_attributes_class_put(); 757 + return ret; 758 + } 759 + 760 + /* ---- Base Driver -------------------------------------------------------- */ 761 + static int tlmi_analyze(void) 762 + { 763 + struct tlmi_pwdcfg pwdcfg; 764 + acpi_status status; 765 + int i, ret; 766 + 767 + if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && 768 + wmi_has_guid(LENOVO_SAVE_BIOS_SETTINGS_GUID)) 769 + tlmi_priv.can_set_bios_settings = true; 770 + 771 + if (wmi_has_guid(LENOVO_GET_BIOS_SELECTIONS_GUID)) 772 + tlmi_priv.can_get_bios_selections = true; 773 + 774 + if (wmi_has_guid(LENOVO_SET_BIOS_PASSWORD_GUID)) 775 + tlmi_priv.can_set_bios_password = true; 776 + 777 + if (wmi_has_guid(LENOVO_BIOS_PASSWORD_SETTINGS_GUID)) 778 + tlmi_priv.can_get_password_settings = true; 779 + 780 + /* 781 + * Try to find the number of valid settings of this machine 782 + * and use it to create sysfs attributes. 783 + */ 784 + for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) { 785 + struct tlmi_attr_setting *setting; 786 + char *item = NULL; 787 + char *p; 788 + 789 + tlmi_priv.setting[i] = NULL; 790 + status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); 791 + if (ACPI_FAILURE(status)) 792 + break; 793 + if (!item) 794 + break; 795 + if (!*item) 796 + continue; 797 + 798 + /* It is not allowed to have '/' for file name. Convert it into '\'. */ 799 + strreplace(item, '/', '\\'); 800 + 801 + /* Remove the value part */ 802 + p = strchrnul(item, ','); 803 + *p = '\0'; 804 + 805 + /* Create a setting entry */ 806 + setting = kzalloc(sizeof(*setting), GFP_KERNEL); 807 + if (!setting) { 808 + ret = -ENOMEM; 809 + goto fail_clear_attr; 810 + } 811 + setting->index = i; 812 + strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN); 813 + /* If BIOS selections supported, load those */ 814 + if (tlmi_priv.can_get_bios_selections) { 815 + ret = tlmi_get_bios_selections(setting->display_name, 816 + &setting->possible_values); 817 + if (ret || !setting->possible_values) 818 + pr_info("Error retrieving possible values for %d : %s\n", 819 + i, setting->display_name); 820 + } 821 + tlmi_priv.setting[i] = setting; 822 + tlmi_priv.settings_count++; 823 + kfree(item); 824 + } 825 + 826 + /* Create password setting structure */ 827 + ret = tlmi_get_pwd_settings(&pwdcfg); 828 + if (ret) 829 + goto fail_clear_attr; 830 + 831 + tlmi_priv.pwd_admin = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 832 + if (!tlmi_priv.pwd_admin) { 833 + ret = -ENOMEM; 834 + goto fail_clear_attr; 835 + } 836 + strscpy(tlmi_priv.pwd_admin->kbdlang, "us", TLMI_LANG_MAXLEN); 837 + tlmi_priv.pwd_admin->encoding = TLMI_ENCODING_ASCII; 838 + tlmi_priv.pwd_admin->pwd_type = "pap"; 839 + tlmi_priv.pwd_admin->role = "bios-admin"; 840 + tlmi_priv.pwd_admin->minlen = pwdcfg.min_length; 841 + if (WARN_ON(pwdcfg.max_length >= TLMI_PWD_BUFSIZE)) 842 + pwdcfg.max_length = TLMI_PWD_BUFSIZE - 1; 843 + tlmi_priv.pwd_admin->maxlen = pwdcfg.max_length; 844 + if (pwdcfg.password_state & TLMI_PAP_PWD) 845 + tlmi_priv.pwd_admin->valid = true; 846 + 847 + tlmi_priv.pwd_power = kzalloc(sizeof(struct tlmi_pwd_setting), GFP_KERNEL); 848 + if (!tlmi_priv.pwd_power) { 849 + ret = -ENOMEM; 850 + goto fail_clear_attr; 851 + } 852 + strscpy(tlmi_priv.pwd_power->kbdlang, "us", TLMI_LANG_MAXLEN); 853 + tlmi_priv.pwd_power->encoding = TLMI_ENCODING_ASCII; 854 + tlmi_priv.pwd_power->pwd_type = "pop"; 855 + tlmi_priv.pwd_power->role = "power-on"; 856 + tlmi_priv.pwd_power->minlen = pwdcfg.min_length; 857 + tlmi_priv.pwd_power->maxlen = pwdcfg.max_length; 858 + 859 + if (pwdcfg.password_state & TLMI_POP_PWD) 860 + tlmi_priv.pwd_power->valid = true; 861 + 862 + return 0; 863 + 864 + fail_clear_attr: 865 + for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) 866 + kfree(tlmi_priv.setting[i]); 867 + return ret; 868 + } 869 + 870 + static void tlmi_remove(struct wmi_device *wdev) 871 + { 872 + tlmi_release_attr(); 873 + device_destroy(fw_attr_class, MKDEV(0, 0)); 874 + fw_attributes_class_put(); 875 + } 876 + 877 + static int tlmi_probe(struct wmi_device *wdev, const void *context) 878 + { 879 + tlmi_analyze(); 880 + return tlmi_sysfs_init(); 881 + } 882 + 883 + static const struct wmi_device_id tlmi_id_table[] = { 884 + { .guid_string = LENOVO_BIOS_SETTING_GUID }, 885 + { } 886 + }; 887 + MODULE_DEVICE_TABLE(wmi, tlmi_id_table); 888 + 889 + static struct wmi_driver tlmi_driver = { 890 + .driver = { 891 + .name = "think-lmi", 892 + }, 893 + .id_table = tlmi_id_table, 894 + .probe = tlmi_probe, 895 + .remove = tlmi_remove, 896 + }; 897 + 898 + MODULE_AUTHOR("Sugumaran L <slacshiminar@lenovo.com>"); 899 + MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>"); 900 + MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>"); 901 + MODULE_DESCRIPTION("ThinkLMI Driver"); 902 + MODULE_LICENSE("GPL"); 903 + 904 + module_wmi_driver(tlmi_driver);
+72
drivers/platform/x86/think-lmi.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef _THINK_LMI_H_ 4 + #define _THINK_LMI_H_ 5 + 6 + #include <linux/types.h> 7 + 8 + #define TLMI_SETTINGS_COUNT 256 9 + #define TLMI_SETTINGS_MAXLEN 512 10 + #define TLMI_PWD_BUFSIZE 129 11 + #define TLMI_LANG_MAXLEN 4 12 + 13 + /* Possible error values */ 14 + struct tlmi_err_codes { 15 + const char *err_str; 16 + int err_code; 17 + }; 18 + 19 + enum encoding_option { 20 + TLMI_ENCODING_ASCII, 21 + TLMI_ENCODING_SCANCODE, 22 + }; 23 + 24 + /* password configuration details */ 25 + struct tlmi_pwdcfg { 26 + uint32_t password_mode; 27 + uint32_t password_state; 28 + uint32_t min_length; 29 + uint32_t max_length; 30 + uint32_t supported_encodings; 31 + uint32_t supported_keyboard; 32 + }; 33 + 34 + /* password setting details */ 35 + struct tlmi_pwd_setting { 36 + struct kobject kobj; 37 + bool valid; 38 + char password[TLMI_PWD_BUFSIZE]; 39 + const char *pwd_type; 40 + const char *role; 41 + int minlen; 42 + int maxlen; 43 + enum encoding_option encoding; 44 + char kbdlang[TLMI_LANG_MAXLEN]; 45 + }; 46 + 47 + /* Attribute setting details */ 48 + struct tlmi_attr_setting { 49 + struct kobject kobj; 50 + int index; 51 + char display_name[TLMI_SETTINGS_MAXLEN]; 52 + char *possible_values; 53 + }; 54 + 55 + struct think_lmi { 56 + struct wmi_device *wmi_device; 57 + 58 + int settings_count; 59 + bool can_set_bios_settings; 60 + bool can_get_bios_selections; 61 + bool can_set_bios_password; 62 + bool can_get_password_settings; 63 + 64 + struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT]; 65 + struct device *class_dev; 66 + struct kset *attribute_kset; 67 + struct kset *authentication_kset; 68 + struct tlmi_pwd_setting *pwd_admin; 69 + struct tlmi_pwd_setting *pwd_power; 70 + }; 71 + 72 + #endif /* !_THINK_LMI_H_ */
+1 -1
drivers/platform/x86/thinkpad_acpi.c
··· 7938 7938 continue; 7939 7939 } else if (sscanf(cmd, "level %u", &l) == 1 && 7940 7940 l >= 0 && l <= TP_EC_VOLUME_MAX) { 7941 - new_level = l; 7941 + new_level = l; 7942 7942 continue; 7943 7943 } 7944 7944 }
+1
drivers/platform/x86/toshiba_acpi.c
··· 2831 2831 2832 2832 if (!dev->info_supported && !dev->system_event_supported) { 2833 2833 pr_warn("No hotkey query interface found\n"); 2834 + error = -EINVAL; 2834 2835 goto err_remove_filter; 2835 2836 } 2836 2837
+1 -1
drivers/platform/x86/toshiba_haps.c
··· 131 131 */ 132 132 static 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 136 acpi_bus_generate_netlink_event(device->pnp.device_class, 137 137 dev_name(&device->dev),
+86 -1
drivers/platform/x86/touchscreen_dmi.c
··· 299 299 .properties = estar_beauty_hd_props, 300 300 }; 301 301 302 + /* Generic props + data for upside-down mounted GDIX1001 touchscreens */ 303 + static const struct property_entry gdix1001_upside_down_props[] = { 304 + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), 305 + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), 306 + { } 307 + }; 308 + 309 + static const struct ts_dmi_data gdix1001_00_upside_down_data = { 310 + .acpi_name = "GDIX1001:00", 311 + .properties = gdix1001_upside_down_props, 312 + }; 313 + 314 + static const struct ts_dmi_data gdix1001_01_upside_down_data = { 315 + .acpi_name = "GDIX1001:01", 316 + .properties = gdix1001_upside_down_props, 317 + }; 318 + 319 + static const struct property_entry glavey_tm800a550l_props[] = { 320 + PROPERTY_ENTRY_STRING("firmware-name", "gt912-glavey-tm800a550l.fw"), 321 + PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-glavey-tm800a550l.cfg"), 322 + PROPERTY_ENTRY_U32("goodix,main-clk", 54), 323 + { } 324 + }; 325 + 326 + static const struct ts_dmi_data glavey_tm800a550l_data = { 327 + .acpi_name = "GDIX1001:00", 328 + .properties = glavey_tm800a550l_props, 329 + }; 330 + 302 331 static const struct property_entry gp_electronic_t701_props[] = { 303 332 PROPERTY_ENTRY_U32("touchscreen-size-x", 960), 304 333 PROPERTY_ENTRY_U32("touchscreen-size-y", 640), ··· 971 942 }, 972 943 }, 973 944 { 974 - /* Chuwi Hi10 Prus (CWI597) */ 945 + /* Chuwi Hi10 Pro (CWI529) */ 975 946 .driver_data = (void *)&chuwi_hi10_pro_data, 976 947 .matches = { 977 948 DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), ··· 1065 1036 .matches = { 1066 1037 DMI_MATCH(DMI_SYS_VENDOR, "Estar"), 1067 1038 DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"), 1039 + }, 1040 + }, 1041 + { /* Glavey TM800A550L */ 1042 + .driver_data = (void *)&glavey_tm800a550l_data, 1043 + .matches = { 1044 + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 1045 + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), 1046 + /* Above strings are too generic, also match on BIOS version */ 1047 + DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), 1068 1048 }, 1069 1049 }, 1070 1050 { ··· 1369 1331 }, 1370 1332 }, 1371 1333 { 1334 + /* Teclast X89 (Android version / BIOS) */ 1335 + .driver_data = (void *)&gdix1001_00_upside_down_data, 1336 + .matches = { 1337 + DMI_MATCH(DMI_BOARD_VENDOR, "WISKY"), 1338 + DMI_MATCH(DMI_BOARD_NAME, "3G062i"), 1339 + }, 1340 + }, 1341 + { 1342 + /* Teclast X89 (Windows version / BIOS) */ 1343 + .driver_data = (void *)&gdix1001_01_upside_down_data, 1344 + .matches = { 1345 + /* tPAD is too generic, also match on bios date */ 1346 + DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), 1347 + DMI_MATCH(DMI_BOARD_NAME, "tPAD"), 1348 + DMI_MATCH(DMI_BIOS_DATE, "12/19/2014"), 1349 + }, 1350 + }, 1351 + { 1372 1352 /* Teclast X98 Plus II */ 1373 1353 .driver_data = (void *)&teclast_x98plus2_data, 1374 1354 .matches = { 1375 1355 DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), 1376 1356 DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"), 1357 + }, 1358 + }, 1359 + { 1360 + /* Teclast X98 Pro */ 1361 + .driver_data = (void *)&gdix1001_00_upside_down_data, 1362 + .matches = { 1363 + /* 1364 + * Only match BIOS date, because the manufacturers 1365 + * BIOS does not report the board name at all 1366 + * (sometimes)... 1367 + */ 1368 + DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), 1369 + DMI_MATCH(DMI_BIOS_DATE, "10/28/2015"), 1377 1370 }, 1378 1371 }, 1379 1372 { ··· 1480 1411 .driver_data = (void *)&vinga_twizzle_j116_data, 1481 1412 .matches = { 1482 1413 DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"), 1414 + }, 1415 + }, 1416 + { 1417 + /* "WinBook TW100" */ 1418 + .driver_data = (void *)&gdix1001_00_upside_down_data, 1419 + .matches = { 1420 + DMI_MATCH(DMI_SYS_VENDOR, "WinBook"), 1421 + DMI_MATCH(DMI_PRODUCT_NAME, "TW100") 1422 + } 1423 + }, 1424 + { 1425 + /* WinBook TW700 */ 1426 + .driver_data = (void *)&gdix1001_00_upside_down_data, 1427 + .matches = { 1428 + DMI_MATCH(DMI_SYS_VENDOR, "WinBook"), 1429 + DMI_MATCH(DMI_PRODUCT_NAME, "TW700") 1483 1430 }, 1484 1431 }, 1485 1432 {
+2 -2
drivers/platform/x86/uv_sysfs.c
··· 778 778 NULL, 779 779 }; 780 780 781 - static struct attribute_group base_attr_group = { 781 + static const struct attribute_group base_attr_group = { 782 782 .attrs = base_attrs 783 783 }; 784 784 ··· 823 823 NULL, 824 824 }; 825 825 826 - static struct attribute_group hubless_base_attr_group = { 826 + static const struct attribute_group hubless_base_attr_group = { 827 827 .attrs = hubless_base_attrs 828 828 }; 829 829
+103
drivers/platform/x86/wireless-hotkey.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Airplane mode button for AMD, HP & Xiaomi laptops 4 + * 5 + * Copyright (C) 2014-2017 Alex Hung <alex.hung@canonical.com> 6 + * Copyright (C) 2021 Advanced Micro Devices 7 + */ 8 + 9 + #include <linux/kernel.h> 10 + #include <linux/module.h> 11 + #include <linux/init.h> 12 + #include <linux/input.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/acpi.h> 15 + #include <acpi/acpi_bus.h> 16 + 17 + MODULE_LICENSE("GPL"); 18 + MODULE_AUTHOR("Alex Hung"); 19 + MODULE_ALIAS("acpi*:HPQ6001:*"); 20 + MODULE_ALIAS("acpi*:WSTADEF:*"); 21 + MODULE_ALIAS("acpi*:AMDI0051:*"); 22 + 23 + static struct input_dev *wl_input_dev; 24 + 25 + static const struct acpi_device_id wl_ids[] = { 26 + {"HPQ6001", 0}, 27 + {"WSTADEF", 0}, 28 + {"AMDI0051", 0}, 29 + {"", 0}, 30 + }; 31 + 32 + static int wireless_input_setup(void) 33 + { 34 + int err; 35 + 36 + wl_input_dev = input_allocate_device(); 37 + if (!wl_input_dev) 38 + return -ENOMEM; 39 + 40 + wl_input_dev->name = "Wireless hotkeys"; 41 + wl_input_dev->phys = "hpq6001/input0"; 42 + wl_input_dev->id.bustype = BUS_HOST; 43 + wl_input_dev->evbit[0] = BIT(EV_KEY); 44 + set_bit(KEY_RFKILL, wl_input_dev->keybit); 45 + 46 + err = input_register_device(wl_input_dev); 47 + if (err) 48 + goto err_free_dev; 49 + 50 + return 0; 51 + 52 + err_free_dev: 53 + input_free_device(wl_input_dev); 54 + return err; 55 + } 56 + 57 + static void wireless_input_destroy(void) 58 + { 59 + input_unregister_device(wl_input_dev); 60 + } 61 + 62 + static void wl_notify(struct acpi_device *acpi_dev, u32 event) 63 + { 64 + if (event != 0x80) { 65 + pr_info("Received unknown event (0x%x)\n", event); 66 + return; 67 + } 68 + 69 + input_report_key(wl_input_dev, KEY_RFKILL, 1); 70 + input_sync(wl_input_dev); 71 + input_report_key(wl_input_dev, KEY_RFKILL, 0); 72 + input_sync(wl_input_dev); 73 + } 74 + 75 + static int wl_add(struct acpi_device *device) 76 + { 77 + int err; 78 + 79 + err = wireless_input_setup(); 80 + if (err) 81 + pr_err("Failed to setup hp wireless hotkeys\n"); 82 + 83 + return err; 84 + } 85 + 86 + static int wl_remove(struct acpi_device *device) 87 + { 88 + wireless_input_destroy(); 89 + return 0; 90 + } 91 + 92 + static struct acpi_driver wl_driver = { 93 + .name = "wireless-hotkey", 94 + .owner = THIS_MODULE, 95 + .ids = wl_ids, 96 + .ops = { 97 + .add = wl_add, 98 + .remove = wl_remove, 99 + .notify = wl_notify, 100 + }, 101 + }; 102 + 103 + module_acpi_driver(wl_driver);
+7
include/linux/acpi.h
··· 1097 1097 #if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) 1098 1098 bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, 1099 1099 struct acpi_resource_gpio **agpio); 1100 + bool acpi_gpio_get_io_resource(struct acpi_resource *ares, 1101 + struct acpi_resource_gpio **agpio); 1100 1102 int acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index); 1101 1103 #else 1102 1104 static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, 1103 1105 struct acpi_resource_gpio **agpio) 1106 + { 1107 + return false; 1108 + } 1109 + static inline bool acpi_gpio_get_io_resource(struct acpi_resource *ares, 1110 + struct acpi_resource_gpio **agpio) 1104 1111 { 1105 1112 return false; 1106 1113 }
+25
include/linux/devm-helpers.h
··· 51 51 return devm_add_action(dev, devm_delayed_work_drop, w); 52 52 } 53 53 54 + static inline void devm_work_drop(void *res) 55 + { 56 + cancel_work_sync(res); 57 + } 58 + 59 + /** 60 + * devm_work_autocancel - Resource-managed work allocation 61 + * @dev: Device which lifetime work is bound to 62 + * @w: Work to be added (and automatically cancelled) 63 + * @worker: Worker function 64 + * 65 + * Initialize work which is automatically cancelled when driver is detached. 66 + * A few drivers need to queue work which must be cancelled before driver 67 + * is detached to avoid accessing removed resources. 68 + * devm_work_autocancel() can be used to omit the explicit 69 + * cancelleation when driver is detached. 70 + */ 71 + static inline int devm_work_autocancel(struct device *dev, 72 + struct work_struct *w, 73 + work_func_t worker) 74 + { 75 + INIT_WORK(w, worker); 76 + return devm_add_action(dev, devm_work_drop, w); 77 + } 78 + 54 79 #endif
+2
include/linux/gpio/consumer.h
··· 692 692 const struct acpi_gpio_mapping *gpios); 693 693 void devm_acpi_dev_remove_driver_gpios(struct device *dev); 694 694 695 + struct gpio_desc *acpi_get_and_request_gpiod(char *path, int pin, char *label); 696 + 695 697 #else /* CONFIG_GPIOLIB && CONFIG_ACPI */ 696 698 697 699 struct acpi_device;
+26 -1
include/linux/surface_aggregator/controller.h
··· 6 6 * managing access and communication to and from the SSAM EC, as well as main 7 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 12 #ifndef _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H ··· 796 796 SSAM_EVENT_REGISTRY(SSAM_SSH_TC_REG, 0x02, 0x01, 0x02) 797 797 798 798 /** 799 + * enum ssam_event_notifier_flags - Flags for event notifiers. 800 + * @SSAM_EVENT_NOTIFIER_OBSERVER: 801 + * The corresponding notifier acts as observer. Registering a notifier 802 + * with this flag set will not attempt to enable any event. Equally, 803 + * unregistering will not attempt to disable any event. Note that a 804 + * notifier with this flag may not even correspond to a certain event at 805 + * all, only to a specific event target category. Event matching will not 806 + * be influenced by this flag. 807 + */ 808 + enum ssam_event_notifier_flags { 809 + SSAM_EVENT_NOTIFIER_OBSERVER = BIT(0), 810 + }; 811 + 812 + /** 799 813 * struct ssam_event_notifier - Notifier block for SSAM events. 800 814 * @base: The base notifier block with callback function and priority. 801 815 * @event: The event for which this block will receive notifications. ··· 817 803 * @event.id: ID specifying the event. 818 804 * @event.mask: Flags determining how events are matched to the notifier. 819 805 * @event.flags: Flags used for enabling the event. 806 + * @flags: Notifier flags (see &enum ssam_event_notifier_flags). 820 807 */ 821 808 struct ssam_event_notifier { 822 809 struct ssam_notifier_block base; ··· 828 813 enum ssam_event_mask mask; 829 814 u8 flags; 830 815 } event; 816 + 817 + unsigned long flags; 831 818 }; 832 819 833 820 int ssam_notifier_register(struct ssam_controller *ctrl, ··· 837 820 838 821 int ssam_notifier_unregister(struct ssam_controller *ctrl, 839 822 struct ssam_event_notifier *n); 823 + 824 + int ssam_controller_event_enable(struct ssam_controller *ctrl, 825 + struct ssam_event_registry reg, 826 + struct ssam_event_id id, u8 flags); 827 + 828 + int ssam_controller_event_disable(struct ssam_controller *ctrl, 829 + struct ssam_event_registry reg, 830 + struct ssam_event_id id, u8 flags); 840 831 841 832 #endif /* _LINUX_SURFACE_AGGREGATOR_CONTROLLER_H */
+1 -1
include/linux/surface_aggregator/device.h
··· 7 7 * Provides support for non-platform/non-ACPI SSAM clients via dedicated 8 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 13 #ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H
+1 -1
include/linux/surface_aggregator/serial_hub.h
··· 6 6 * Surface System Aggregator Module (SSAM). Provides the interface for basic 7 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 12 #ifndef _LINUX_SURFACE_AGGREGATOR_SERIAL_HUB_H
+71 -2
include/uapi/linux/surface_aggregator/cdev.h
··· 6 6 * device. This device provides direct user-space access to the SSAM EC. 7 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 12 #ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H ··· 73 73 } response; 74 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 + */ 88 + struct ssam_cdev_notifier_desc { 89 + __s32 priority; 90 + __u8 target_category; 91 + } __attribute__((__packed__)); 92 + 93 + /** 94 + * struct ssam_cdev_event_desc - Event descriptor. 95 + * @reg: Registry via which the event will be enabled/disabled. 96 + * @reg.target_category: Target category for the event registry requests. 97 + * @reg.target_id: Target ID for the event registry requests. 98 + * @reg.cid_enable: Command ID for the event-enable request. 99 + * @reg.cid_disable: Command ID for the event-disable request. 100 + * @id: ID specifying the event. 101 + * @id.target_category: Target category of the event source. 102 + * @id.instance: Instance ID of the event source. 103 + * @flags: Flags used for enabling the event. 104 + * 105 + * Specifies which event should be enabled/disabled and how to do that. 106 + */ 107 + struct ssam_cdev_event_desc { 108 + struct { 109 + __u8 target_category; 110 + __u8 target_id; 111 + __u8 cid_enable; 112 + __u8 cid_disable; 113 + } reg; 114 + 115 + struct { 116 + __u8 target_category; 117 + __u8 instance; 118 + } id; 119 + 120 + __u8 flags; 121 + } __attribute__((__packed__)); 122 + 123 + /** 124 + * struct ssam_cdev_event - SSAM event sent by the EC. 125 + * @target_category: Target category of the event source. See &enum ssam_ssh_tc. 126 + * @target_id: Target ID of the event source. 127 + * @command_id: Command ID of the event. 128 + * @instance_id: Instance ID of the event source. 129 + * @length: Length of the event payload in bytes. 130 + * @data: Event payload data. 131 + */ 132 + struct ssam_cdev_event { 133 + __u8 target_category; 134 + __u8 target_id; 135 + __u8 command_id; 136 + __u8 instance_id; 137 + __u16 length; 138 + __u8 data[]; 139 + } __attribute__((__packed__)); 140 + 141 + #define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request) 142 + #define SSAM_CDEV_NOTIF_REGISTER _IOW(0xA5, 2, struct ssam_cdev_notifier_desc) 143 + #define SSAM_CDEV_NOTIF_UNREGISTER _IOW(0xA5, 3, struct ssam_cdev_notifier_desc) 144 + #define SSAM_CDEV_EVENT_ENABLE _IOW(0xA5, 4, struct ssam_cdev_event_desc) 145 + #define SSAM_CDEV_EVENT_DISABLE _IOW(0xA5, 5, struct ssam_cdev_event_desc) 77 146 78 147 #endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */
+17 -1
tools/power/x86/intel-speed-select/isst-config.c
··· 15 15 int arg; 16 16 }; 17 17 18 - static const char *version_str = "v1.9"; 18 + static const char *version_str = "v1.10"; 19 19 static const int supported_api_ver = 1; 20 20 static struct isst_if_platform_info isst_platform_info; 21 21 static char *progname; ··· 101 101 int is_skx_based_platform(void) 102 102 { 103 103 if (cpu_model == 0x55) 104 + return 1; 105 + 106 + return 0; 107 + } 108 + 109 + int is_spr_platform(void) 110 + { 111 + if (cpu_model == 0x8F) 112 + return 1; 113 + 114 + return 0; 115 + } 116 + 117 + int is_icx_platform(void) 118 + { 119 + if (cpu_model == 0x6A || cpu_model == 0x6C) 104 120 return 1; 105 121 106 122 return 0;
+15
tools/power/x86/intel-speed-select/isst-core.c
··· 201 201 { 202 202 unsigned int resp; 203 203 int ret; 204 + 204 205 ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, 205 206 0, config_index, &resp); 206 207 if (ret) { ··· 210 209 } 211 210 212 211 ctdp_level->mem_freq = resp & GENMASK(7, 0); 212 + if (is_spr_platform()) { 213 + ctdp_level->mem_freq *= 200; 214 + } else if (is_icx_platform()) { 215 + if (ctdp_level->mem_freq < 7) { 216 + ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; 217 + ctdp_level->mem_freq /= 10; 218 + if (ctdp_level->mem_freq % 10 > 5) 219 + ctdp_level->mem_freq++; 220 + } else { 221 + ctdp_level->mem_freq = 0; 222 + } 223 + } else { 224 + ctdp_level->mem_freq = 0; 225 + } 213 226 debug_printf( 214 227 "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", 215 228 cpu, config_index, resp, ctdp_level->mem_freq);
+1 -1
tools/power/x86/intel-speed-select/isst-display.c
··· 446 446 if (ctdp_level->mem_freq) { 447 447 snprintf(header, sizeof(header), "mem-frequency(MHz)"); 448 448 snprintf(value, sizeof(value), "%d", 449 - ctdp_level->mem_freq * DISP_FREQ_MULTIPLIER); 449 + ctdp_level->mem_freq); 450 450 format_and_print(outf, level + 2, header, value); 451 451 } 452 452
+2
tools/power/x86/intel-speed-select/isst.h
··· 257 257 extern int isst_read_pm_config(int cpu, int *cp_state, int *cp_cap); 258 258 extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg); 259 259 extern int is_skx_based_platform(void); 260 + extern int is_spr_platform(void); 261 + extern int is_icx_platform(void); 260 262 extern void isst_trl_display_information(int cpu, FILE *outf, unsigned long long trl); 261 263 #endif