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

Merge tag 'platform-drivers-x86-v4.15-1' of git://git.infradead.org/linux-platform-drivers-x86

Pull x86 platform driver updates from Andy Shevchenko:
"Here is the collected material against Platform Drivers x86 subsystem.
It's rather bit busy cycle for PDx86, mostly due to Dell SMBIOS driver
activity

For this cycle we have quite an update for the Dell SMBIOS driver
including WMI work to provide an interface for SMBIOS tokens via sysfs
and WMI support for 2017+ Dell laptop models. SMM dispatcher code is
split into a separate driver followed by a new WMI dispatcher. The
latter provides a character device interface to user space.

The git history also contains a merge of immutable branch from Wolfram
Sang in order to apply a dependent fix to the Intel CherryTrail
Battery Management driver.

Other Intel drivers got a lot of cleanups. The Turbo Boost Max 3.0
support is added for Intel Skylake.

Peaq WMI hotkeys driver gets its own maintainer and white list of
supported models.

Silead DMI is expanded to support few additional platforms.

Tablet mode via GMMS ACPI method is added to support some ThinkPad
tablets.

new driver:
- Add driver to force WMI Thunderbolt controller power status

asus-wmi:
- Add lightbar led support

dell-laptop:
- Allocate buffer before rfkill use

dell-smbios:
- fix string overflow
- Add filtering support
- Introduce dispatcher for SMM calls
- Add a sysfs interface for SMBIOS tokens
- only run if proper oem string is detected
- Prefix class/select with cmd_
- Add pr_fmt definition to driver

dell-smbios-smm:
- test for WSMT

dell-smbios-wmi:
- release mutex lock on WMI call failure
- introduce userspace interface
- Add new WMI dispatcher driver

dell-smo8800:
- remove redundant assignments to byte_data

dell-wmi:
- don't check length returned
- clean up wmi descriptor check
- increase severity of some failures
- Do not match on descriptor GUID modalias
- Label driver as handling notifications

dell-*wmi*:
- Relay failed initial probe to dependent drivers

dell-wmi-descriptor:
- check if memory was allocated
- split WMI descriptor into it's own driver

fujitsu-laptop:
- Fix radio LED detection
- Don't oops when FUJ02E3 is not presnt

hp_accel:
- Add quirk for HP ProBook 440 G4

hp-wmi:
- Fix tablet mode detection for convertibles

ideapad-laptop:
- Add Lenovo Yoga 920-13IKB to no_hw_rfkill dmi list

intel_cht_int33fe:
- Update fusb302 type string, add properties
- make a couple of local functions static
- Work around BIOS bug on some devices

intel-hid:
- Power button suspend on Dell Latitude 7275

intel_ips:
- Convert timers to use timer_setup()
- Remove FSF address from GPL notice
- Remove unneeded fields and label
- Keep pointer to struct device
- Use PCI_VDEVICE() macro
- Switch to new PCI IRQ allocation API
- Simplify error handling via devres API

intel_pmc_ipc:
- Revert Use MFD framework to create dependent devices
- Use MFD framework to create dependent devices
- Use spin_lock to protect GCR updates
- Use devm_* calls in driver probe function

intel_punit_ipc:
- Fix resource ioremap warning

intel_telemetry:
- Remove useless default in Kconfig
- Add needed inclusion
- cleanup redundant headers
- Fix typos
- Fix load failure info

intel_telemetry_debugfs:
- Use standard ARRAY_SIZE() macro

intel_turbo_max_3:
- Add Skylake platform

intel-wmi-thunderbolt:
- Silence error cases

mlx-platform:
- make a couple of structures static

peaq_wmi:
- Fix missing terminating entry for peaq_dmi_table

peaq-wmi:
- Remove unnecessary checks from peaq_wmi_exit
- Add DMI check before binding to the WMI interface
- Revert Blacklist Lenovo ideapad 700-15ISK
- Blacklist Lenovo ideapad 700-15ISK

silead_dmi:
- Add silead, home-button property to some tablets
- Add entry for the Digma e200 tablet
- Fix GP-electronic T701 entry
- Add entry for the Chuwi Hi8 Pro tablet

sony-laptop:
- Drop variable assignment in sony_nc_setup_rfkill()
- Fix error handling in sony_nc_setup_rfkill()

thinkpad_acpi:
- Implement tablet mode using GMMS method

tools/wmi:
- add a sample for dell smbios communication over WMI

wmi:
- release mutex on module acquistion failure
- create userspace interface for drivers
- Don't allow drivers to get each other's GUIDs
- Add new method wmidev_evaluate_method
- Destroy on cleanup rather than unregister
- Cleanup exit routine in reverse order of init
- Sort include list"

* tag 'platform-drivers-x86-v4.15-1' of git://git.infradead.org/linux-platform-drivers-x86: (74 commits)
platform/x86: silead_dmi: Add silead, home-button property to some tablets
platform/x86: dell-laptop: Allocate buffer before rfkill use
platform/x86: dell-*wmi*: Relay failed initial probe to dependent drivers
platform/x86: dell-wmi-descriptor: check if memory was allocated
platform/x86: Revert intel_pmc_ipc: Use MFD framework to create dependent devices
platform/x86: dell-smbios-wmi: release mutex lock on WMI call failure
platform/x86: wmi: release mutex on module acquistion failure
platform/x86: dell-smbios: fix string overflow
platform/x86: intel_pmc_ipc: Use MFD framework to create dependent devices
platform/x86: intel_punit_ipc: Fix resource ioremap warning
platform/x86: dell-smo8800: remove redundant assignments to byte_data
platform/x86: hp-wmi: Fix tablet mode detection for convertibles
platform/x86: intel_ips: Convert timers to use timer_setup()
platform/x86: sony-laptop: Drop variable assignment in sony_nc_setup_rfkill()
platform/x86: sony-laptop: Fix error handling in sony_nc_setup_rfkill()
tools/wmi: add a sample for dell smbios communication over WMI
platform/x86: dell-smbios-wmi: introduce userspace interface
platform/x86: wmi: create userspace interface for drivers
platform/x86: dell-smbios: Add filtering support
platform/x86: dell-smbios-smm: test for WSMT
...

+2618 -557
+41
Documentation/ABI/testing/dell-smbios-wmi
··· 1 + What: /dev/wmi/dell-smbios 2 + Date: November 2017 3 + KernelVersion: 4.15 4 + Contact: "Mario Limonciello" <mario.limonciello@dell.com> 5 + Description: 6 + Perform SMBIOS calls on supported Dell machines. 7 + through the Dell ACPI-WMI interface. 8 + 9 + IOCTL's and buffer formats are defined in: 10 + <uapi/linux/wmi.h> 11 + 12 + 1) To perform an SMBIOS call from userspace, you'll need to 13 + first determine the minimum size of the calling interface 14 + buffer for your machine. 15 + Platforms that contain larger buffers can return larger 16 + objects from the system firmware. 17 + Commonly this size is either 4k or 32k. 18 + 19 + To determine the size of the buffer read() a u64 dword from 20 + the WMI character device /dev/wmi/dell-smbios. 21 + 22 + 2) After you've determined the minimum size of the calling 23 + interface buffer, you can allocate a structure that represents 24 + the structure documented above. 25 + 26 + 3) In the 'length' object store the size of the buffer you 27 + determined above and allocated. 28 + 29 + 4) In this buffer object, prepare as necessary for the SMBIOS 30 + call you're interested in. Typically SMBIOS buffers have 31 + "class", "select", and "input" defined to values that coincide 32 + with the data you are interested in. 33 + Documenting class/select/input values is outside of the scope 34 + of this documentation. Check with the libsmbios project for 35 + further documentation on these values. 36 + 37 + 6) Run the call by using ioctl() as described in the header. 38 + 39 + 7) The output will be returned in the buffer object. 40 + 41 + 8) Be sure to free up your allocated object.
+21
Documentation/ABI/testing/sysfs-platform-dell-smbios
··· 1 + What: /sys/devices/platform/<platform>/tokens/* 2 + Date: November 2017 3 + KernelVersion: 4.15 4 + Contact: "Mario Limonciello" <mario.limonciello@dell.com> 5 + Description: 6 + A read-only description of Dell platform tokens 7 + available on the machine. 8 + 9 + Each token attribute is available as a pair of 10 + sysfs attributes readable by a process with 11 + CAP_SYS_ADMIN. 12 + 13 + For example the token ID "5" would be available 14 + as the following attributes: 15 + 16 + 0005_location 17 + 0005_value 18 + 19 + Tokens will vary from machine to machine, and 20 + only tokens available on that machine will be 21 + displayed.
+11
Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt
··· 1 + What: /sys/devices/platform/<platform>/force_power 2 + Date: September 2017 3 + KernelVersion: 4.15 4 + Contact: "Mario Limonciello" <mario.limonciello@dell.com> 5 + Description: 6 + Modify the platform force power state, influencing 7 + Thunderbolt controllers to turn on or off when no 8 + devices are connected (write-only) 9 + There are two available states: 10 + * 0 -> Force power disabled 11 + * 1 -> Force power enabled
+15
Documentation/admin-guide/thunderbolt.rst
··· 221 221 port which are named like ``thunderbolt0`` and so on. From this point 222 222 you can either use standard userspace tools like ``ifconfig`` to 223 223 configure the interface or let your GUI to handle it automatically. 224 + 225 + Forcing power 226 + ------------- 227 + Many OEMs include a method that can be used to force the power of a 228 + thunderbolt controller to an "On" state even if nothing is connected. 229 + If supported by your machine this will be exposed by the WMI bus with 230 + a sysfs attribute called "force_power". 231 + 232 + For example the intel-wmi-thunderbolt driver exposes this attribute in: 233 + /sys/devices/platform/PNP0C14:00/wmi_bus/wmi_bus-PNP0C14:00/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power 234 + 235 + To force the power to on, write 1 to this attribute file. 236 + To disable force power, write 0 to this attribute file. 237 + 238 + Note: it's currently not possible to query the force power state of a platform.
+38 -1
MAINTAINERS
··· 384 384 L: platform-driver-x86@vger.kernel.org 385 385 S: Orphan 386 386 F: drivers/platform/x86/wmi.c 387 + F: include/uapi/linux/wmi.h 387 388 388 389 AD1889 ALSA SOUND DRIVER 389 390 M: Thibaut Varene <T-Bone@parisc-linux.org> ··· 4031 4030 S: Maintained 4032 4031 F: drivers/net/fddi/defxx.* 4033 4032 4033 + DELL SMBIOS DRIVER 4034 + M: Pali Rohár <pali.rohar@gmail.com> 4035 + M: Mario Limonciello <mario.limonciello@dell.com> 4036 + L: platform-driver-x86@vger.kernel.org 4037 + S: Maintained 4038 + F: drivers/platform/x86/dell-smbios.* 4039 + 4040 + DELL SMBIOS SMM DRIVER 4041 + M: Mario Limonciello <mario.limonciello@dell.com> 4042 + L: platform-driver-x86@vger.kernel.org 4043 + S: Maintained 4044 + F: drivers/platform/x86/dell-smbios-smm.c 4045 + 4046 + DELL SMBIOS WMI DRIVER 4047 + M: Mario Limonciello <mario.limonciello@dell.com> 4048 + L: platform-driver-x86@vger.kernel.org 4049 + S: Maintained 4050 + F: drivers/platform/x86/dell-smbios-wmi.c 4051 + F: tools/wmi/dell-smbios-example.c 4052 + 4034 4053 DELL LAPTOP DRIVER 4035 4054 M: Matthew Garrett <mjg59@srcf.ucam.org> 4036 4055 M: Pali Rohár <pali.rohar@gmail.com> ··· 4080 4059 F: Documentation/dcdbas.txt 4081 4060 F: drivers/firmware/dcdbas.* 4082 4061 4083 - DELL WMI EXTRAS DRIVER 4062 + DELL WMI NOTIFICATIONS DRIVER 4084 4063 M: Matthew Garrett <mjg59@srcf.ucam.org> 4085 4064 M: Pali Rohár <pali.rohar@gmail.com> 4086 4065 S: Maintained 4087 4066 F: drivers/platform/x86/dell-wmi.c 4067 + 4068 + DELL WMI DESCRIPTOR DRIVER 4069 + M: Mario Limonciello <mario.limonciello@dell.com> 4070 + S: Maintained 4071 + F: drivers/platform/x86/dell-wmi-descriptor.c 4088 4072 4089 4073 DELTA ST MEDIA DRIVER 4090 4074 M: Hugues Fruchet <hugues.fruchet@st.com> ··· 7206 7180 F: Documentation/wimax/README.i2400m 7207 7181 F: drivers/net/wimax/i2400m/ 7208 7182 F: include/uapi/linux/wimax/i2400m.h 7183 + 7184 + INTEL WMI THUNDERBOLT FORCE POWER DRIVER 7185 + M: Mario Limonciello <mario.limonciello@dell.com> 7186 + S: Maintained 7187 + F: drivers/platform/x86/intel-wmi-thunderbolt.c 7209 7188 7210 7189 INTEL(R) TRACE HUB 7211 7190 M: Alexander Shishkin <alexander.shishkin@linux.intel.com> ··· 10660 10629 S: Maintained 10661 10630 F: crypto/pcrypt.c 10662 10631 F: include/crypto/pcrypt.h 10632 + 10633 + PEAQ WMI HOTKEYS DRIVER 10634 + M: Hans de Goede <hdegoede@redhat.com> 10635 + L: platform-driver-x86@vger.kernel.org 10636 + S: Maintained 10637 + F: drivers/platform/x86/peaq-wmi.c 10663 10638 10664 10639 PER-CPU MEMORY ALLOCATOR 10665 10640 M: Tejun Heo <tj@kernel.org>
+50 -8
drivers/platform/x86/Kconfig
··· 93 93 94 94 config DELL_SMBIOS 95 95 tristate 96 - select DCDBAS 97 - ---help--- 98 - This module provides common functions for kernel modules using 99 - Dell SMBIOS. 100 96 101 - If you have a Dell laptop, say Y or M here. 97 + config DELL_SMBIOS_WMI 98 + tristate "Dell SMBIOS calling interface (WMI implementation)" 99 + depends on ACPI_WMI 100 + select DELL_WMI_DESCRIPTOR 101 + default ACPI_WMI 102 + select DELL_SMBIOS 103 + ---help--- 104 + This provides an implementation for the Dell SMBIOS calling interface 105 + communicated over ACPI-WMI. 106 + 107 + If you have a Dell computer from >2007 you should say Y or M here. 108 + If you aren't sure and this module doesn't work for your computer 109 + it just won't load. 110 + 111 + config DELL_SMBIOS_SMM 112 + tristate "Dell SMBIOS calling interface (SMM implementation)" 113 + depends on DCDBAS 114 + default DCDBAS 115 + select DELL_SMBIOS 116 + ---help--- 117 + This provides an implementation for the Dell SMBIOS calling interface 118 + communicated over SMI/SMM. 119 + 120 + If you have a Dell computer from <=2017 you should say Y or M here. 121 + If you aren't sure and this module doesn't work for your computer 122 + it just won't load. 102 123 103 124 config DELL_LAPTOP 104 125 tristate "Dell Laptop Extras" ··· 137 116 laptops (except for some models covered by the Compal driver). 138 117 139 118 config DELL_WMI 140 - tristate "Dell WMI extras" 119 + tristate "Dell WMI notifications" 141 120 depends on ACPI_WMI 142 121 depends on DMI 143 122 depends on INPUT 144 123 depends on ACPI_VIDEO || ACPI_VIDEO = n 124 + select DELL_WMI_DESCRIPTOR 145 125 select DELL_SMBIOS 146 126 select INPUT_SPARSEKMAP 147 127 ---help--- ··· 150 128 151 129 To compile this driver as a module, choose M here: the module will 152 130 be called dell-wmi. 131 + 132 + config DELL_WMI_DESCRIPTOR 133 + tristate 134 + depends on ACPI_WMI 153 135 154 136 config DELL_WMI_AIO 155 137 tristate "WMI Hotkeys for Dell All-In-One series" ··· 684 658 To compile this driver as a module, choose M here: the module will 685 659 be called wmi-bmof. 686 660 661 + config INTEL_WMI_THUNDERBOLT 662 + tristate "Intel WMI thunderbolt force power driver" 663 + depends on ACPI_WMI 664 + default ACPI_WMI 665 + ---help--- 666 + Say Y here if you want to be able to use the WMI interface on select 667 + systems to force the power control of Intel Thunderbolt controllers. 668 + This is useful for updating the firmware when devices are not plugged 669 + into the controller. 670 + 671 + To compile this driver as a module, choose M here: the module will 672 + be called intel-wmi-thunderbolt. 673 + 687 674 config MSI_WMI 688 675 tristate "MSI WMI extras" 689 676 depends on ACPI_WMI ··· 832 793 833 794 config INTEL_CHT_INT33FE 834 795 tristate "Intel Cherry Trail ACPI INT33FE Driver" 835 - depends on X86 && ACPI && I2C 796 + depends on X86 && ACPI && I2C && REGULATOR 836 797 ---help--- 837 798 This driver add support for the INT33FE ACPI device found on 838 799 some Intel Cherry Trail devices. ··· 842 803 FUSB302 USB Type-C Controller and PI3USB30532 USB switch. 843 804 This driver instantiates i2c-clients for these, so that standard 844 805 i2c drivers for these chips can bind to the them. 806 + 807 + If you enable this driver it is advised to also select 808 + CONFIG_TYPEC_FUSB302=m, CONFIG_CHARGER_BQ24190=m and 809 + CONFIG_BATTERY_MAX17042=m. 845 810 846 811 config INTEL_INT0002_VGPIO 847 812 tristate "Intel ACPI INT0002 Virtual GPIO driver" ··· 1131 1088 1132 1089 config INTEL_TELEMETRY 1133 1090 tristate "Intel SoC Telemetry Driver" 1134 - default n 1135 1091 depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64 1136 1092 ---help--- 1137 1093 This driver provides interfaces to configure and use
+4
drivers/platform/x86/Makefile
··· 13 13 obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o 14 14 obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o 15 15 obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o 16 + obj-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o 17 + obj-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o 16 18 obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o 17 19 obj-$(CONFIG_DELL_WMI) += dell-wmi.o 20 + obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o 18 21 obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o 19 22 obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o 20 23 obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o ··· 43 40 obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o 44 41 obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o 45 42 obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o 43 + obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o 46 44 47 45 # toshiba_acpi must link after wmi to ensure that wmi devices are found 48 46 # before toshiba_acpi initializes
+63
drivers/platform/x86/asus-wmi.c
··· 119 119 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 120 120 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 121 121 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ 122 + #define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 122 123 123 124 /* Misc */ 124 125 #define ASUS_WMI_DEVID_CAMERA 0x00060013 ··· 149 148 #define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 150 149 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF 151 150 #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 151 + #define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F 152 152 153 153 #define ASUS_FAN_DESC "cpu_fan" 154 154 #define ASUS_FAN_MFUN 0x13 ··· 224 222 int tpd_led_wk; 225 223 struct led_classdev kbd_led; 226 224 int kbd_led_wk; 225 + struct led_classdev lightbar_led; 226 + int lightbar_led_wk; 227 227 struct workqueue_struct *led_workqueue; 228 228 struct work_struct tpd_led_work; 229 229 struct work_struct kbd_led_work; 230 230 struct work_struct wlan_led_work; 231 + struct work_struct lightbar_led_work; 231 232 232 233 struct asus_rfkill wlan; 233 234 struct asus_rfkill bluetooth; ··· 572 567 return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK; 573 568 } 574 569 570 + static void lightbar_led_update(struct work_struct *work) 571 + { 572 + struct asus_wmi *asus; 573 + int ctrl_param; 574 + 575 + asus = container_of(work, struct asus_wmi, lightbar_led_work); 576 + 577 + ctrl_param = asus->lightbar_led_wk; 578 + asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL); 579 + } 580 + 581 + static void lightbar_led_set(struct led_classdev *led_cdev, 582 + enum led_brightness value) 583 + { 584 + struct asus_wmi *asus; 585 + 586 + asus = container_of(led_cdev, struct asus_wmi, lightbar_led); 587 + 588 + asus->lightbar_led_wk = !!value; 589 + queue_work(asus->led_workqueue, &asus->lightbar_led_work); 590 + } 591 + 592 + static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev) 593 + { 594 + struct asus_wmi *asus; 595 + u32 result; 596 + 597 + asus = container_of(led_cdev, struct asus_wmi, lightbar_led); 598 + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); 599 + 600 + return result & ASUS_WMI_DSTS_LIGHTBAR_MASK; 601 + } 602 + 603 + static int lightbar_led_presence(struct asus_wmi *asus) 604 + { 605 + u32 result; 606 + 607 + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); 608 + 609 + return result & ASUS_WMI_DSTS_PRESENCE_BIT; 610 + } 611 + 575 612 static void asus_wmi_led_exit(struct asus_wmi *asus) 576 613 { 577 614 if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) ··· 622 575 led_classdev_unregister(&asus->tpd_led); 623 576 if (!IS_ERR_OR_NULL(asus->wlan_led.dev)) 624 577 led_classdev_unregister(&asus->wlan_led); 578 + if (!IS_ERR_OR_NULL(asus->lightbar_led.dev)) 579 + led_classdev_unregister(&asus->lightbar_led); 625 580 if (asus->led_workqueue) 626 581 destroy_workqueue(asus->led_workqueue); 627 582 } ··· 679 630 680 631 rv = led_classdev_register(&asus->platform_device->dev, 681 632 &asus->wlan_led); 633 + if (rv) 634 + goto error; 635 + } 636 + 637 + if (lightbar_led_presence(asus)) { 638 + INIT_WORK(&asus->lightbar_led_work, lightbar_led_update); 639 + 640 + asus->lightbar_led.name = "asus::lightbar"; 641 + asus->lightbar_led.brightness_set = lightbar_led_set; 642 + asus->lightbar_led.brightness_get = lightbar_led_get; 643 + asus->lightbar_led.max_brightness = 1; 644 + 645 + rv = led_classdev_register(&asus->platform_device->dev, 646 + &asus->lightbar_led); 682 647 } 683 648 684 649 error:
+105 -177
drivers/platform/x86/dell-laptop.c
··· 35 35 #include "dell-rbtn.h" 36 36 #include "dell-smbios.h" 37 37 38 - #define BRIGHTNESS_TOKEN 0x7d 39 - #define KBD_LED_OFF_TOKEN 0x01E1 40 - #define KBD_LED_ON_TOKEN 0x01E2 41 - #define KBD_LED_AUTO_TOKEN 0x01E3 42 - #define KBD_LED_AUTO_25_TOKEN 0x02EA 43 - #define KBD_LED_AUTO_50_TOKEN 0x02EB 44 - #define KBD_LED_AUTO_75_TOKEN 0x02EC 45 - #define KBD_LED_AUTO_100_TOKEN 0x02F6 46 - #define GLOBAL_MIC_MUTE_ENABLE 0x0364 47 - #define GLOBAL_MIC_MUTE_DISABLE 0x0365 48 - #define KBD_LED_AC_TOKEN 0x0451 49 - 50 38 struct quirk_entry { 51 39 u8 touchpad_led; 52 40 ··· 73 85 } 74 86 }; 75 87 88 + static struct calling_interface_buffer *buffer; 76 89 static struct platform_device *platform_device; 77 90 static struct backlight_device *dell_backlight_device; 78 91 static struct rfkill *wifi_rfkill; ··· 272 283 { } 273 284 }; 274 285 286 + void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3) 287 + { 288 + memset(buffer, 0, sizeof(struct calling_interface_buffer)); 289 + buffer->input[0] = arg0; 290 + buffer->input[1] = arg1; 291 + buffer->input[2] = arg2; 292 + buffer->input[3] = arg3; 293 + } 294 + 295 + int dell_send_request(u16 class, u16 select) 296 + { 297 + int ret; 298 + 299 + buffer->cmd_class = class; 300 + buffer->cmd_select = select; 301 + ret = dell_smbios_call(buffer); 302 + if (ret != 0) 303 + return ret; 304 + return dell_smbios_error(buffer->output[0]); 305 + } 306 + 275 307 /* 276 308 * Derived from information in smbios-wireless-ctl: 277 309 * ··· 415 405 416 406 static int dell_rfkill_set(void *data, bool blocked) 417 407 { 418 - struct calling_interface_buffer *buffer; 419 408 int disable = blocked ? 1 : 0; 420 409 unsigned long radio = (unsigned long)data; 421 410 int hwswitch_bit = (unsigned long)data - 1; ··· 422 413 int status; 423 414 int ret; 424 415 425 - buffer = dell_smbios_get_buffer(); 426 - 427 - dell_smbios_send_request(17, 11); 428 - ret = buffer->output[0]; 416 + dell_set_arguments(0, 0, 0, 0); 417 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 418 + if (ret) 419 + return ret; 429 420 status = buffer->output[1]; 430 421 431 - if (ret != 0) 432 - goto out; 433 - 434 - dell_smbios_clear_buffer(); 435 - 436 - buffer->input[0] = 0x2; 437 - dell_smbios_send_request(17, 11); 438 - ret = buffer->output[0]; 422 + dell_set_arguments(0x2, 0, 0, 0); 423 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 424 + if (ret) 425 + return ret; 439 426 hwswitch = buffer->output[1]; 440 427 441 428 /* If the hardware switch controls this radio, and the hardware ··· 440 435 (status & BIT(0)) && !(status & BIT(16))) 441 436 disable = 1; 442 437 443 - dell_smbios_clear_buffer(); 444 - 445 - buffer->input[0] = (1 | (radio<<8) | (disable << 16)); 446 - dell_smbios_send_request(17, 11); 447 - ret = buffer->output[0]; 448 - 449 - out: 450 - dell_smbios_release_buffer(); 451 - return dell_smbios_error(ret); 438 + dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0); 439 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 440 + return ret; 452 441 } 453 442 454 - /* Must be called with the buffer held */ 455 443 static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio, 456 - int status, 457 - struct calling_interface_buffer *buffer) 444 + int status) 458 445 { 459 446 if (status & BIT(0)) { 460 447 /* Has hw-switch, sync sw_state to BIOS */ 461 448 int block = rfkill_blocked(rfkill); 462 - dell_smbios_clear_buffer(); 463 - buffer->input[0] = (1 | (radio << 8) | (block << 16)); 464 - dell_smbios_send_request(17, 11); 449 + dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0); 450 + dell_send_request(CLASS_INFO, SELECT_RFKILL); 465 451 } else { 466 452 /* No hw-switch, sync BIOS state to sw_state */ 467 453 rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16))); ··· 468 472 469 473 static void dell_rfkill_query(struct rfkill *rfkill, void *data) 470 474 { 471 - struct calling_interface_buffer *buffer; 472 475 int radio = ((unsigned long)data & 0xF); 473 476 int hwswitch; 474 477 int status; 475 478 int ret; 476 479 477 - buffer = dell_smbios_get_buffer(); 478 - 479 - dell_smbios_send_request(17, 11); 480 - ret = buffer->output[0]; 480 + dell_set_arguments(0, 0, 0, 0); 481 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 481 482 status = buffer->output[1]; 482 483 483 484 if (ret != 0 || !(status & BIT(0))) { 484 - dell_smbios_release_buffer(); 485 485 return; 486 486 } 487 487 488 - dell_smbios_clear_buffer(); 489 - 490 - buffer->input[0] = 0x2; 491 - dell_smbios_send_request(17, 11); 492 - ret = buffer->output[0]; 488 + dell_set_arguments(0, 0x2, 0, 0); 489 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 493 490 hwswitch = buffer->output[1]; 494 - 495 - dell_smbios_release_buffer(); 496 491 497 492 if (ret != 0) 498 493 return; ··· 500 513 501 514 static int dell_debugfs_show(struct seq_file *s, void *data) 502 515 { 503 - struct calling_interface_buffer *buffer; 504 516 int hwswitch_state; 505 517 int hwswitch_ret; 506 518 int status; 507 519 int ret; 508 520 509 - buffer = dell_smbios_get_buffer(); 510 - 511 - dell_smbios_send_request(17, 11); 512 - ret = buffer->output[0]; 521 + dell_set_arguments(0, 0, 0, 0); 522 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 523 + if (ret) 524 + return ret; 513 525 status = buffer->output[1]; 514 526 515 - dell_smbios_clear_buffer(); 516 - 517 - buffer->input[0] = 0x2; 518 - dell_smbios_send_request(17, 11); 519 - hwswitch_ret = buffer->output[0]; 527 + dell_set_arguments(0, 0x2, 0, 0); 528 + hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 529 + if (hwswitch_ret) 530 + return hwswitch_ret; 520 531 hwswitch_state = buffer->output[1]; 521 - 522 - dell_smbios_release_buffer(); 523 532 524 533 seq_printf(s, "return:\t%d\n", ret); 525 534 seq_printf(s, "status:\t0x%X\n", status); ··· 596 613 597 614 static void dell_update_rfkill(struct work_struct *ignored) 598 615 { 599 - struct calling_interface_buffer *buffer; 600 616 int hwswitch = 0; 601 617 int status; 602 618 int ret; 603 619 604 - buffer = dell_smbios_get_buffer(); 605 - 606 - dell_smbios_send_request(17, 11); 607 - ret = buffer->output[0]; 620 + dell_set_arguments(0, 0, 0, 0); 621 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 608 622 status = buffer->output[1]; 609 623 610 624 if (ret != 0) 611 - goto out; 625 + return; 612 626 613 - dell_smbios_clear_buffer(); 614 - 615 - buffer->input[0] = 0x2; 616 - dell_smbios_send_request(17, 11); 617 - ret = buffer->output[0]; 627 + dell_set_arguments(0, 0x2, 0, 0); 628 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 618 629 619 630 if (ret == 0 && (status & BIT(0))) 620 631 hwswitch = buffer->output[1]; 621 632 622 633 if (wifi_rfkill) { 623 634 dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch); 624 - dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer); 635 + dell_rfkill_update_sw_state(wifi_rfkill, 1, status); 625 636 } 626 637 if (bluetooth_rfkill) { 627 638 dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status, 628 639 hwswitch); 629 - dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status, 630 - buffer); 640 + dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status); 631 641 } 632 642 if (wwan_rfkill) { 633 643 dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch); 634 - dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer); 644 + dell_rfkill_update_sw_state(wwan_rfkill, 3, status); 635 645 } 636 - 637 - out: 638 - dell_smbios_release_buffer(); 639 646 } 640 647 static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); 641 648 ··· 669 696 670 697 static int __init dell_setup_rfkill(void) 671 698 { 672 - struct calling_interface_buffer *buffer; 673 699 int status, ret, whitelisted; 674 700 const char *product; 675 701 ··· 684 712 if (!force_rfkill && !whitelisted) 685 713 return 0; 686 714 687 - buffer = dell_smbios_get_buffer(); 688 - dell_smbios_send_request(17, 11); 689 - ret = buffer->output[0]; 715 + dell_set_arguments(0, 0, 0, 0); 716 + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); 690 717 status = buffer->output[1]; 691 - dell_smbios_release_buffer(); 692 718 693 719 /* dell wireless info smbios call is not supported */ 694 720 if (ret != 0) ··· 839 869 840 870 static int dell_send_intensity(struct backlight_device *bd) 841 871 { 842 - struct calling_interface_buffer *buffer; 843 872 struct calling_interface_token *token; 844 873 int ret; 845 874 ··· 846 877 if (!token) 847 878 return -ENODEV; 848 879 849 - buffer = dell_smbios_get_buffer(); 850 - buffer->input[0] = token->location; 851 - buffer->input[1] = bd->props.brightness; 852 - 880 + dell_set_arguments(token->location, bd->props.brightness, 0, 0); 853 881 if (power_supply_is_system_supplied() > 0) 854 - dell_smbios_send_request(1, 2); 882 + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC); 855 883 else 856 - dell_smbios_send_request(1, 1); 884 + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT); 857 885 858 - ret = dell_smbios_error(buffer->output[0]); 859 - 860 - dell_smbios_release_buffer(); 861 886 return ret; 862 887 } 863 888 864 889 static int dell_get_intensity(struct backlight_device *bd) 865 890 { 866 - struct calling_interface_buffer *buffer; 867 891 struct calling_interface_token *token; 868 892 int ret; 869 893 ··· 864 902 if (!token) 865 903 return -ENODEV; 866 904 867 - buffer = dell_smbios_get_buffer(); 868 - buffer->input[0] = token->location; 869 - 905 + dell_set_arguments(token->location, 0, 0, 0); 870 906 if (power_supply_is_system_supplied() > 0) 871 - dell_smbios_send_request(0, 2); 907 + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC); 872 908 else 873 - dell_smbios_send_request(0, 1); 909 + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT); 874 910 875 - if (buffer->output[0]) 876 - ret = dell_smbios_error(buffer->output[0]); 877 - else 911 + if (ret == 0) 878 912 ret = buffer->output[1]; 879 - 880 - dell_smbios_release_buffer(); 881 913 return ret; 882 914 } 883 915 ··· 1135 1179 1136 1180 static int kbd_get_info(struct kbd_info *info) 1137 1181 { 1138 - struct calling_interface_buffer *buffer; 1139 1182 u8 units; 1140 1183 int ret; 1141 1184 1142 - buffer = dell_smbios_get_buffer(); 1143 - 1144 - buffer->input[0] = 0x0; 1145 - dell_smbios_send_request(4, 11); 1146 - ret = buffer->output[0]; 1147 - 1148 - if (ret) { 1149 - ret = dell_smbios_error(ret); 1150 - goto out; 1151 - } 1185 + dell_set_arguments(0, 0, 0, 0); 1186 + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); 1187 + if (ret) 1188 + return ret; 1152 1189 1153 1190 info->modes = buffer->output[1] & 0xFFFF; 1154 1191 info->type = (buffer->output[1] >> 24) & 0xFF; ··· 1158 1209 if (units & BIT(3)) 1159 1210 info->days = (buffer->output[3] >> 24) & 0xFF; 1160 1211 1161 - out: 1162 - dell_smbios_release_buffer(); 1163 1212 return ret; 1164 1213 } 1165 1214 ··· 1216 1269 1217 1270 static int kbd_get_state(struct kbd_state *state) 1218 1271 { 1219 - struct calling_interface_buffer *buffer; 1220 1272 int ret; 1221 1273 1222 - buffer = dell_smbios_get_buffer(); 1223 - 1224 - buffer->input[0] = 0x1; 1225 - dell_smbios_send_request(4, 11); 1226 - ret = buffer->output[0]; 1227 - 1228 - if (ret) { 1229 - ret = dell_smbios_error(ret); 1230 - goto out; 1231 - } 1274 + dell_set_arguments(0x1, 0, 0, 0); 1275 + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); 1276 + if (ret) 1277 + return ret; 1232 1278 1233 1279 state->mode_bit = ffs(buffer->output[1] & 0xFFFF); 1234 1280 if (state->mode_bit != 0) ··· 1236 1296 state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F; 1237 1297 state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3; 1238 1298 1239 - out: 1240 - dell_smbios_release_buffer(); 1241 1299 return ret; 1242 1300 } 1243 1301 1244 1302 static int kbd_set_state(struct kbd_state *state) 1245 1303 { 1246 - struct calling_interface_buffer *buffer; 1247 1304 int ret; 1305 + u32 input1; 1306 + u32 input2; 1248 1307 1249 - buffer = dell_smbios_get_buffer(); 1250 - buffer->input[0] = 0x2; 1251 - buffer->input[1] = BIT(state->mode_bit) & 0xFFFF; 1252 - buffer->input[1] |= (state->triggers & 0xFF) << 16; 1253 - buffer->input[1] |= (state->timeout_value & 0x3F) << 24; 1254 - buffer->input[1] |= (state->timeout_unit & 0x3) << 30; 1255 - buffer->input[2] = state->als_setting & 0xFF; 1256 - buffer->input[2] |= (state->level & 0xFF) << 16; 1257 - buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24; 1258 - buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30; 1259 - dell_smbios_send_request(4, 11); 1260 - ret = buffer->output[0]; 1261 - dell_smbios_release_buffer(); 1308 + input1 = BIT(state->mode_bit) & 0xFFFF; 1309 + input1 |= (state->triggers & 0xFF) << 16; 1310 + input1 |= (state->timeout_value & 0x3F) << 24; 1311 + input1 |= (state->timeout_unit & 0x3) << 30; 1312 + input2 = state->als_setting & 0xFF; 1313 + input2 |= (state->level & 0xFF) << 16; 1314 + input2 |= (state->timeout_value_ac & 0x3F) << 24; 1315 + input2 |= (state->timeout_unit_ac & 0x3) << 30; 1316 + dell_set_arguments(0x2, input1, input2, 0); 1317 + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); 1262 1318 1263 - return dell_smbios_error(ret); 1319 + return ret; 1264 1320 } 1265 1321 1266 1322 static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) ··· 1281 1345 1282 1346 static int kbd_set_token_bit(u8 bit) 1283 1347 { 1284 - struct calling_interface_buffer *buffer; 1285 1348 struct calling_interface_token *token; 1286 1349 int ret; 1287 1350 ··· 1291 1356 if (!token) 1292 1357 return -EINVAL; 1293 1358 1294 - buffer = dell_smbios_get_buffer(); 1295 - buffer->input[0] = token->location; 1296 - buffer->input[1] = token->value; 1297 - dell_smbios_send_request(1, 0); 1298 - ret = buffer->output[0]; 1299 - dell_smbios_release_buffer(); 1359 + dell_set_arguments(token->location, token->value, 0, 0); 1360 + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); 1300 1361 1301 - return dell_smbios_error(ret); 1362 + return ret; 1302 1363 } 1303 1364 1304 1365 static int kbd_get_token_bit(u8 bit) 1305 1366 { 1306 - struct calling_interface_buffer *buffer; 1307 1367 struct calling_interface_token *token; 1308 1368 int ret; 1309 1369 int val; ··· 1310 1380 if (!token) 1311 1381 return -EINVAL; 1312 1382 1313 - buffer = dell_smbios_get_buffer(); 1314 - buffer->input[0] = token->location; 1315 - dell_smbios_send_request(0, 0); 1316 - ret = buffer->output[0]; 1383 + dell_set_arguments(token->location, 0, 0, 0); 1384 + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD); 1317 1385 val = buffer->output[1]; 1318 - dell_smbios_release_buffer(); 1319 1386 1320 1387 if (ret) 1321 - return dell_smbios_error(ret); 1388 + return ret; 1322 1389 1323 1390 return (val == token->value); 1324 1391 } ··· 2029 2102 2030 2103 int dell_micmute_led_set(int state) 2031 2104 { 2032 - struct calling_interface_buffer *buffer; 2033 2105 struct calling_interface_token *token; 2034 2106 2035 2107 if (state == 0) ··· 2041 2115 if (!token) 2042 2116 return -ENODEV; 2043 2117 2044 - buffer = dell_smbios_get_buffer(); 2045 - buffer->input[0] = token->location; 2046 - buffer->input[1] = token->value; 2047 - dell_smbios_send_request(1, 0); 2048 - dell_smbios_release_buffer(); 2118 + dell_set_arguments(token->location, token->value, 0, 0); 2119 + dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); 2049 2120 2050 2121 return state; 2051 2122 } ··· 2050 2127 2051 2128 static int __init dell_init(void) 2052 2129 { 2053 - struct calling_interface_buffer *buffer; 2054 2130 struct calling_interface_token *token; 2055 2131 int max_intensity = 0; 2056 2132 int ret; ··· 2072 2150 ret = platform_device_add(platform_device); 2073 2151 if (ret) 2074 2152 goto fail_platform_device2; 2153 + 2154 + buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); 2155 + if (!buffer) 2156 + goto fail_buffer; 2157 + 2075 2158 2076 2159 ret = dell_setup_rfkill(); 2077 2160 ··· 2102 2175 2103 2176 token = dell_smbios_find_token(BRIGHTNESS_TOKEN); 2104 2177 if (token) { 2105 - buffer = dell_smbios_get_buffer(); 2106 - buffer->input[0] = token->location; 2107 - dell_smbios_send_request(0, 2); 2108 - if (buffer->output[0] == 0) 2178 + dell_set_arguments(token->location, 0, 0, 0); 2179 + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC); 2180 + if (ret) 2109 2181 max_intensity = buffer->output[3]; 2110 - dell_smbios_release_buffer(); 2111 2182 } 2112 2183 2113 2184 if (max_intensity) { ··· 2139 2214 fail_get_brightness: 2140 2215 backlight_device_unregister(dell_backlight_device); 2141 2216 fail_backlight: 2217 + kfree(buffer); 2218 + fail_buffer: 2142 2219 dell_cleanup_rfkill(); 2143 2220 fail_rfkill: 2144 2221 platform_device_del(platform_device); ··· 2160 2233 touchpad_led_exit(); 2161 2234 kbd_led_exit(); 2162 2235 backlight_device_unregister(dell_backlight_device); 2236 + kfree(buffer); 2163 2237 dell_cleanup_rfkill(); 2164 2238 if (platform_device) { 2165 2239 platform_device_unregister(platform_device);
+196
drivers/platform/x86/dell-smbios-smm.c
··· 1 + /* 2 + * SMI methods for use with dell-smbios 3 + * 4 + * Copyright (c) Red Hat <mjg@redhat.com> 5 + * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com> 6 + * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com> 7 + * Copyright (c) 2017 Dell Inc. 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + */ 13 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 + 15 + #include <linux/dmi.h> 16 + #include <linux/gfp.h> 17 + #include <linux/io.h> 18 + #include <linux/module.h> 19 + #include <linux/mutex.h> 20 + #include <linux/platform_device.h> 21 + #include "../../firmware/dcdbas.h" 22 + #include "dell-smbios.h" 23 + 24 + static int da_command_address; 25 + static int da_command_code; 26 + static struct calling_interface_buffer *buffer; 27 + struct platform_device *platform_device; 28 + static DEFINE_MUTEX(smm_mutex); 29 + 30 + static const struct dmi_system_id dell_device_table[] __initconst = { 31 + { 32 + .ident = "Dell laptop", 33 + .matches = { 34 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 35 + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 36 + }, 37 + }, 38 + { 39 + .matches = { 40 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 41 + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ 42 + }, 43 + }, 44 + { 45 + .matches = { 46 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 47 + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ 48 + }, 49 + }, 50 + { 51 + .ident = "Dell Computer Corporation", 52 + .matches = { 53 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 54 + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 55 + }, 56 + }, 57 + { } 58 + }; 59 + MODULE_DEVICE_TABLE(dmi, dell_device_table); 60 + 61 + static void __init parse_da_table(const struct dmi_header *dm) 62 + { 63 + struct calling_interface_structure *table = 64 + container_of(dm, struct calling_interface_structure, header); 65 + 66 + /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least 67 + * 6 bytes of entry 68 + */ 69 + if (dm->length < 17) 70 + return; 71 + 72 + da_command_address = table->cmdIOAddress; 73 + da_command_code = table->cmdIOCode; 74 + } 75 + 76 + static void __init find_cmd_address(const struct dmi_header *dm, void *dummy) 77 + { 78 + switch (dm->type) { 79 + case 0xda: /* Calling interface */ 80 + parse_da_table(dm); 81 + break; 82 + } 83 + } 84 + 85 + int dell_smbios_smm_call(struct calling_interface_buffer *input) 86 + { 87 + struct smi_cmd command; 88 + size_t size; 89 + 90 + size = sizeof(struct calling_interface_buffer); 91 + command.magic = SMI_CMD_MAGIC; 92 + command.command_address = da_command_address; 93 + command.command_code = da_command_code; 94 + command.ebx = virt_to_phys(buffer); 95 + command.ecx = 0x42534931; 96 + 97 + mutex_lock(&smm_mutex); 98 + memcpy(buffer, input, size); 99 + dcdbas_smi_request(&command); 100 + memcpy(input, buffer, size); 101 + mutex_unlock(&smm_mutex); 102 + return 0; 103 + } 104 + 105 + /* When enabled this indicates that SMM won't work */ 106 + static bool test_wsmt_enabled(void) 107 + { 108 + struct calling_interface_token *wsmt; 109 + 110 + /* if token doesn't exist, SMM will work */ 111 + wsmt = dell_smbios_find_token(WSMT_EN_TOKEN); 112 + if (!wsmt) 113 + return false; 114 + 115 + /* If token exists, try to access over SMM but set a dummy return. 116 + * - If WSMT disabled it will be overwritten by SMM 117 + * - If WSMT enabled then dummy value will remain 118 + */ 119 + buffer->cmd_class = CLASS_TOKEN_READ; 120 + buffer->cmd_select = SELECT_TOKEN_STD; 121 + memset(buffer, 0, sizeof(struct calling_interface_buffer)); 122 + buffer->input[0] = wsmt->location; 123 + buffer->output[0] = 99; 124 + dell_smbios_smm_call(buffer); 125 + if (buffer->output[0] == 99) 126 + return true; 127 + 128 + return false; 129 + } 130 + 131 + static int __init dell_smbios_smm_init(void) 132 + { 133 + int ret; 134 + /* 135 + * Allocate buffer below 4GB for SMI data--only 32-bit physical addr 136 + * is passed to SMI handler. 137 + */ 138 + buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); 139 + if (!buffer) 140 + return -ENOMEM; 141 + 142 + dmi_walk(find_cmd_address, NULL); 143 + 144 + if (test_wsmt_enabled()) { 145 + pr_debug("Disabling due to WSMT enabled\n"); 146 + ret = -ENODEV; 147 + goto fail_wsmt; 148 + } 149 + 150 + platform_device = platform_device_alloc("dell-smbios", 1); 151 + if (!platform_device) { 152 + ret = -ENOMEM; 153 + goto fail_platform_device_alloc; 154 + } 155 + 156 + ret = platform_device_add(platform_device); 157 + if (ret) 158 + goto fail_platform_device_add; 159 + 160 + ret = dell_smbios_register_device(&platform_device->dev, 161 + &dell_smbios_smm_call); 162 + if (ret) 163 + goto fail_register; 164 + 165 + return 0; 166 + 167 + fail_register: 168 + platform_device_del(platform_device); 169 + 170 + fail_platform_device_add: 171 + platform_device_put(platform_device); 172 + 173 + fail_wsmt: 174 + fail_platform_device_alloc: 175 + free_page((unsigned long)buffer); 176 + return ret; 177 + } 178 + 179 + static void __exit dell_smbios_smm_exit(void) 180 + { 181 + if (platform_device) { 182 + dell_smbios_unregister_device(&platform_device->dev); 183 + platform_device_unregister(platform_device); 184 + free_page((unsigned long)buffer); 185 + } 186 + } 187 + 188 + subsys_initcall(dell_smbios_smm_init); 189 + module_exit(dell_smbios_smm_exit); 190 + 191 + MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 192 + MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>"); 193 + MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); 194 + MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 195 + MODULE_DESCRIPTION("Dell SMBIOS communications over SMI"); 196 + MODULE_LICENSE("GPL");
+272
drivers/platform/x86/dell-smbios-wmi.c
··· 1 + /* 2 + * WMI methods for use with dell-smbios 3 + * 4 + * Copyright (c) 2017 Dell Inc. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 + 12 + #include <linux/dmi.h> 13 + #include <linux/list.h> 14 + #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/uaccess.h> 17 + #include <linux/wmi.h> 18 + #include "dell-smbios.h" 19 + #include "dell-wmi-descriptor.h" 20 + 21 + static DEFINE_MUTEX(call_mutex); 22 + static DEFINE_MUTEX(list_mutex); 23 + static int wmi_supported; 24 + 25 + struct misc_bios_flags_structure { 26 + struct dmi_header header; 27 + u16 flags0; 28 + } __packed; 29 + #define FLAG_HAS_ACPI_WMI 0x02 30 + 31 + #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" 32 + 33 + struct wmi_smbios_priv { 34 + struct dell_wmi_smbios_buffer *buf; 35 + struct list_head list; 36 + struct wmi_device *wdev; 37 + struct device *child; 38 + u32 req_buf_size; 39 + }; 40 + static LIST_HEAD(wmi_list); 41 + 42 + static inline struct wmi_smbios_priv *get_first_smbios_priv(void) 43 + { 44 + return list_first_entry_or_null(&wmi_list, 45 + struct wmi_smbios_priv, 46 + list); 47 + } 48 + 49 + static int run_smbios_call(struct wmi_device *wdev) 50 + { 51 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 52 + struct wmi_smbios_priv *priv; 53 + struct acpi_buffer input; 54 + union acpi_object *obj; 55 + acpi_status status; 56 + 57 + priv = dev_get_drvdata(&wdev->dev); 58 + input.length = priv->req_buf_size - sizeof(u64); 59 + input.pointer = &priv->buf->std; 60 + 61 + dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n", 62 + priv->buf->std.cmd_class, priv->buf->std.cmd_select, 63 + priv->buf->std.input[0], priv->buf->std.input[1], 64 + priv->buf->std.input[2], priv->buf->std.input[3]); 65 + 66 + status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); 67 + if (ACPI_FAILURE(status)) 68 + return -EIO; 69 + obj = (union acpi_object *)output.pointer; 70 + if (obj->type != ACPI_TYPE_BUFFER) { 71 + dev_dbg(&wdev->dev, "received type: %d\n", obj->type); 72 + if (obj->type == ACPI_TYPE_INTEGER) 73 + dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n", 74 + obj->integer.value); 75 + return -EIO; 76 + } 77 + memcpy(&priv->buf->std, obj->buffer.pointer, obj->buffer.length); 78 + dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n", 79 + priv->buf->std.output[0], priv->buf->std.output[1], 80 + priv->buf->std.output[2], priv->buf->std.output[3]); 81 + 82 + return 0; 83 + } 84 + 85 + int dell_smbios_wmi_call(struct calling_interface_buffer *buffer) 86 + { 87 + struct wmi_smbios_priv *priv; 88 + size_t difference; 89 + size_t size; 90 + int ret; 91 + 92 + mutex_lock(&call_mutex); 93 + priv = get_first_smbios_priv(); 94 + if (!priv) { 95 + ret = -ENODEV; 96 + goto out_wmi_call; 97 + } 98 + 99 + size = sizeof(struct calling_interface_buffer); 100 + difference = priv->req_buf_size - sizeof(u64) - size; 101 + 102 + memset(&priv->buf->ext, 0, difference); 103 + memcpy(&priv->buf->std, buffer, size); 104 + ret = run_smbios_call(priv->wdev); 105 + memcpy(buffer, &priv->buf->std, size); 106 + out_wmi_call: 107 + mutex_unlock(&call_mutex); 108 + 109 + return ret; 110 + } 111 + 112 + static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd, 113 + struct wmi_ioctl_buffer *arg) 114 + { 115 + struct wmi_smbios_priv *priv; 116 + int ret = 0; 117 + 118 + switch (cmd) { 119 + case DELL_WMI_SMBIOS_CMD: 120 + mutex_lock(&call_mutex); 121 + priv = dev_get_drvdata(&wdev->dev); 122 + if (!priv) { 123 + ret = -ENODEV; 124 + goto fail_smbios_cmd; 125 + } 126 + memcpy(priv->buf, arg, priv->req_buf_size); 127 + if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) { 128 + dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n", 129 + priv->buf->std.cmd_class, 130 + priv->buf->std.cmd_select, 131 + priv->buf->std.input[0]); 132 + ret = -EFAULT; 133 + goto fail_smbios_cmd; 134 + } 135 + ret = run_smbios_call(priv->wdev); 136 + if (ret) 137 + goto fail_smbios_cmd; 138 + memcpy(arg, priv->buf, priv->req_buf_size); 139 + fail_smbios_cmd: 140 + mutex_unlock(&call_mutex); 141 + break; 142 + default: 143 + ret = -ENOIOCTLCMD; 144 + } 145 + return ret; 146 + } 147 + 148 + static int dell_smbios_wmi_probe(struct wmi_device *wdev) 149 + { 150 + struct wmi_smbios_priv *priv; 151 + int count; 152 + int ret; 153 + 154 + ret = dell_wmi_get_descriptor_valid(); 155 + if (ret) 156 + return ret; 157 + 158 + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv), 159 + GFP_KERNEL); 160 + if (!priv) 161 + return -ENOMEM; 162 + 163 + /* WMI buffer size will be either 4k or 32k depending on machine */ 164 + if (!dell_wmi_get_size(&priv->req_buf_size)) 165 + return -EPROBE_DEFER; 166 + 167 + /* add in the length object we will use internally with ioctl */ 168 + priv->req_buf_size += sizeof(u64); 169 + ret = set_required_buffer_size(wdev, priv->req_buf_size); 170 + if (ret) 171 + return ret; 172 + 173 + count = get_order(priv->req_buf_size); 174 + priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); 175 + if (!priv->buf) 176 + return -ENOMEM; 177 + 178 + /* ID is used by dell-smbios to set priority of drivers */ 179 + wdev->dev.id = 1; 180 + ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call); 181 + if (ret) 182 + goto fail_register; 183 + 184 + priv->wdev = wdev; 185 + dev_set_drvdata(&wdev->dev, priv); 186 + mutex_lock(&list_mutex); 187 + list_add_tail(&priv->list, &wmi_list); 188 + mutex_unlock(&list_mutex); 189 + 190 + return 0; 191 + 192 + fail_register: 193 + free_pages((unsigned long)priv->buf, count); 194 + return ret; 195 + } 196 + 197 + static int dell_smbios_wmi_remove(struct wmi_device *wdev) 198 + { 199 + struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev); 200 + int count; 201 + 202 + mutex_lock(&call_mutex); 203 + mutex_lock(&list_mutex); 204 + list_del(&priv->list); 205 + mutex_unlock(&list_mutex); 206 + dell_smbios_unregister_device(&wdev->dev); 207 + count = get_order(priv->req_buf_size); 208 + free_pages((unsigned long)priv->buf, count); 209 + mutex_unlock(&call_mutex); 210 + return 0; 211 + } 212 + 213 + static const struct wmi_device_id dell_smbios_wmi_id_table[] = { 214 + { .guid_string = DELL_WMI_SMBIOS_GUID }, 215 + { }, 216 + }; 217 + 218 + static void __init parse_b1_table(const struct dmi_header *dm) 219 + { 220 + struct misc_bios_flags_structure *flags = 221 + container_of(dm, struct misc_bios_flags_structure, header); 222 + 223 + /* 4 bytes header, 8 bytes flags */ 224 + if (dm->length < 12) 225 + return; 226 + if (dm->handle != 0xb100) 227 + return; 228 + if ((flags->flags0 & FLAG_HAS_ACPI_WMI)) 229 + wmi_supported = 1; 230 + } 231 + 232 + static void __init find_b1(const struct dmi_header *dm, void *dummy) 233 + { 234 + switch (dm->type) { 235 + case 0xb1: /* misc bios flags */ 236 + parse_b1_table(dm); 237 + break; 238 + } 239 + } 240 + 241 + static struct wmi_driver dell_smbios_wmi_driver = { 242 + .driver = { 243 + .name = "dell-smbios", 244 + }, 245 + .probe = dell_smbios_wmi_probe, 246 + .remove = dell_smbios_wmi_remove, 247 + .id_table = dell_smbios_wmi_id_table, 248 + .filter_callback = dell_smbios_wmi_filter, 249 + }; 250 + 251 + static int __init init_dell_smbios_wmi(void) 252 + { 253 + dmi_walk(find_b1, NULL); 254 + 255 + if (!wmi_supported) 256 + return -ENODEV; 257 + 258 + return wmi_driver_register(&dell_smbios_wmi_driver); 259 + } 260 + 261 + static void __exit exit_dell_smbios_wmi(void) 262 + { 263 + wmi_driver_unregister(&dell_smbios_wmi_driver); 264 + } 265 + 266 + module_init(init_dell_smbios_wmi); 267 + module_exit(exit_dell_smbios_wmi); 268 + 269 + MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID); 270 + MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 271 + MODULE_DESCRIPTION("Dell SMBIOS communications over WMI"); 272 + MODULE_LICENSE("GPL");
+466 -52
drivers/platform/x86/dell-smbios.c
··· 12 12 * it under the terms of the GNU General Public License version 2 as 13 13 * published by the Free Software Foundation. 14 14 */ 15 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 16 16 17 #include <linux/kernel.h> 17 18 #include <linux/module.h> 19 + #include <linux/capability.h> 18 20 #include <linux/dmi.h> 19 21 #include <linux/err.h> 20 - #include <linux/gfp.h> 21 22 #include <linux/mutex.h> 23 + #include <linux/platform_device.h> 22 24 #include <linux/slab.h> 23 - #include <linux/io.h> 24 - #include "../../firmware/dcdbas.h" 25 25 #include "dell-smbios.h" 26 26 27 - struct calling_interface_structure { 28 - struct dmi_header header; 29 - u16 cmdIOAddress; 30 - u8 cmdIOCode; 31 - u32 supportedCmds; 32 - struct calling_interface_token tokens[]; 33 - } __packed; 34 - 35 - static struct calling_interface_buffer *buffer; 36 - static DEFINE_MUTEX(buffer_mutex); 37 - 38 - static int da_command_address; 39 - static int da_command_code; 27 + static u32 da_supported_commands; 40 28 static int da_num_tokens; 29 + static struct platform_device *platform_device; 41 30 static struct calling_interface_token *da_tokens; 31 + static struct device_attribute *token_location_attrs; 32 + static struct device_attribute *token_value_attrs; 33 + static struct attribute **token_attrs; 34 + static DEFINE_MUTEX(smbios_mutex); 35 + 36 + struct smbios_device { 37 + struct list_head list; 38 + struct device *device; 39 + int (*call_fn)(struct calling_interface_buffer *); 40 + }; 41 + 42 + struct smbios_call { 43 + u32 need_capability; 44 + int cmd_class; 45 + int cmd_select; 46 + }; 47 + 48 + /* calls that are whitelisted for given capabilities */ 49 + static struct smbios_call call_whitelist[] = { 50 + /* generally tokens are allowed, but may be further filtered or 51 + * restricted by token blacklist or whitelist 52 + */ 53 + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD}, 54 + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC}, 55 + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT}, 56 + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD}, 57 + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC}, 58 + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT}, 59 + /* used by userspace: fwupdate */ 60 + {CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP}, 61 + /* used by userspace: fwupd */ 62 + {CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK}, 63 + {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE}, 64 + }; 65 + 66 + /* calls that are explicitly blacklisted */ 67 + static struct smbios_call call_blacklist[] = { 68 + {0x0000, 01, 07}, /* manufacturing use */ 69 + {0x0000, 06, 05}, /* manufacturing use */ 70 + {0x0000, 11, 03}, /* write once */ 71 + {0x0000, 11, 07}, /* write once */ 72 + {0x0000, 11, 11}, /* write once */ 73 + {0x0000, 19, -1}, /* diagnostics */ 74 + /* handled by kernel: dell-laptop */ 75 + {0x0000, CLASS_INFO, SELECT_RFKILL}, 76 + {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT}, 77 + }; 78 + 79 + struct token_range { 80 + u32 need_capability; 81 + u16 min; 82 + u16 max; 83 + }; 84 + 85 + /* tokens that are whitelisted for given capabilities */ 86 + static struct token_range token_whitelist[] = { 87 + /* used by userspace: fwupdate */ 88 + {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN}, 89 + /* can indicate to userspace that WMI is needed */ 90 + {0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN} 91 + }; 92 + 93 + /* tokens that are explicitly blacklisted */ 94 + static struct token_range token_blacklist[] = { 95 + {0x0000, 0x0058, 0x0059}, /* ME use */ 96 + {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */ 97 + {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */ 98 + {0x0000, 0x0175, 0x0176}, /* write once */ 99 + {0x0000, 0x0195, 0x0197}, /* diagnostics */ 100 + {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */ 101 + {0x0000, 0x027D, 0x0284}, /* diagnostics */ 102 + {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */ 103 + {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */ 104 + {0x0000, 0x0300, 0x0302}, /* manufacturing use */ 105 + {0x0000, 0x0325, 0x0326}, /* manufacturing use */ 106 + {0x0000, 0x0332, 0x0335}, /* fan control */ 107 + {0x0000, 0x0350, 0x0350}, /* manufacturing use */ 108 + {0x0000, 0x0363, 0x0363}, /* manufacturing use */ 109 + {0x0000, 0x0368, 0x0368}, /* manufacturing use */ 110 + {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */ 111 + {0x0000, 0x049E, 0x049F}, /* manufacturing use */ 112 + {0x0000, 0x04A0, 0x04A3}, /* disagnostics */ 113 + {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */ 114 + {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */ 115 + {0x0000, 0x9000, 0x9001}, /* internal BIOS use */ 116 + {0x0000, 0xA000, 0xBFFF}, /* write only */ 117 + {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */ 118 + /* handled by kernel: dell-laptop */ 119 + {0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN}, 120 + {0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN}, 121 + {0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN}, 122 + {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN}, 123 + {0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN}, 124 + {0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE}, 125 + }; 126 + 127 + static LIST_HEAD(smbios_device_list); 42 128 43 129 int dell_smbios_error(int value) 44 130 { ··· 141 55 } 142 56 EXPORT_SYMBOL_GPL(dell_smbios_error); 143 57 144 - struct calling_interface_buffer *dell_smbios_get_buffer(void) 58 + int dell_smbios_register_device(struct device *d, void *call_fn) 145 59 { 146 - mutex_lock(&buffer_mutex); 147 - dell_smbios_clear_buffer(); 148 - return buffer; 149 - } 150 - EXPORT_SYMBOL_GPL(dell_smbios_get_buffer); 60 + struct smbios_device *priv; 151 61 152 - void dell_smbios_clear_buffer(void) 62 + priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL); 63 + if (!priv) 64 + return -ENOMEM; 65 + get_device(d); 66 + priv->device = d; 67 + priv->call_fn = call_fn; 68 + mutex_lock(&smbios_mutex); 69 + list_add_tail(&priv->list, &smbios_device_list); 70 + mutex_unlock(&smbios_mutex); 71 + dev_dbg(d, "Added device: %s\n", d->driver->name); 72 + return 0; 73 + } 74 + EXPORT_SYMBOL_GPL(dell_smbios_register_device); 75 + 76 + void dell_smbios_unregister_device(struct device *d) 153 77 { 154 - memset(buffer, 0, sizeof(struct calling_interface_buffer)); 155 - } 156 - EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer); 78 + struct smbios_device *priv; 157 79 158 - void dell_smbios_release_buffer(void) 80 + mutex_lock(&smbios_mutex); 81 + list_for_each_entry(priv, &smbios_device_list, list) { 82 + if (priv->device == d) { 83 + list_del(&priv->list); 84 + put_device(d); 85 + break; 86 + } 87 + } 88 + mutex_unlock(&smbios_mutex); 89 + dev_dbg(d, "Remove device: %s\n", d->driver->name); 90 + } 91 + EXPORT_SYMBOL_GPL(dell_smbios_unregister_device); 92 + 93 + int dell_smbios_call_filter(struct device *d, 94 + struct calling_interface_buffer *buffer) 159 95 { 160 - mutex_unlock(&buffer_mutex); 161 - } 162 - EXPORT_SYMBOL_GPL(dell_smbios_release_buffer); 96 + u16 t = 0; 97 + int i; 163 98 164 - void dell_smbios_send_request(int class, int select) 99 + /* can't make calls over 30 */ 100 + if (buffer->cmd_class > 30) { 101 + dev_dbg(d, "class too big: %u\n", buffer->cmd_class); 102 + return -EINVAL; 103 + } 104 + 105 + /* supported calls on the particular system */ 106 + if (!(da_supported_commands & (1 << buffer->cmd_class))) { 107 + dev_dbg(d, "invalid command, supported commands: 0x%8x\n", 108 + da_supported_commands); 109 + return -EINVAL; 110 + } 111 + 112 + /* match against call blacklist */ 113 + for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) { 114 + if (buffer->cmd_class != call_blacklist[i].cmd_class) 115 + continue; 116 + if (buffer->cmd_select != call_blacklist[i].cmd_select && 117 + call_blacklist[i].cmd_select != -1) 118 + continue; 119 + dev_dbg(d, "blacklisted command: %u/%u\n", 120 + buffer->cmd_class, buffer->cmd_select); 121 + return -EINVAL; 122 + } 123 + 124 + /* if a token call, find token ID */ 125 + 126 + if ((buffer->cmd_class == CLASS_TOKEN_READ || 127 + buffer->cmd_class == CLASS_TOKEN_WRITE) && 128 + buffer->cmd_select < 3) { 129 + /* find the matching token ID */ 130 + for (i = 0; i < da_num_tokens; i++) { 131 + if (da_tokens[i].location != buffer->input[0]) 132 + continue; 133 + t = da_tokens[i].tokenID; 134 + break; 135 + } 136 + 137 + /* token call; but token didn't exist */ 138 + if (!t) { 139 + dev_dbg(d, "token at location %04x doesn't exist\n", 140 + buffer->input[0]); 141 + return -EINVAL; 142 + } 143 + 144 + /* match against token blacklist */ 145 + for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) { 146 + if (!token_blacklist[i].min || !token_blacklist[i].max) 147 + continue; 148 + if (t >= token_blacklist[i].min && 149 + t <= token_blacklist[i].max) 150 + return -EINVAL; 151 + } 152 + 153 + /* match against token whitelist */ 154 + for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) { 155 + if (!token_whitelist[i].min || !token_whitelist[i].max) 156 + continue; 157 + if (t < token_whitelist[i].min || 158 + t > token_whitelist[i].max) 159 + continue; 160 + if (!token_whitelist[i].need_capability || 161 + capable(token_whitelist[i].need_capability)) { 162 + dev_dbg(d, "whitelisted token: %x\n", t); 163 + return 0; 164 + } 165 + 166 + } 167 + } 168 + /* match against call whitelist */ 169 + for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) { 170 + if (buffer->cmd_class != call_whitelist[i].cmd_class) 171 + continue; 172 + if (buffer->cmd_select != call_whitelist[i].cmd_select) 173 + continue; 174 + if (!call_whitelist[i].need_capability || 175 + capable(call_whitelist[i].need_capability)) { 176 + dev_dbg(d, "whitelisted capable command: %u/%u\n", 177 + buffer->cmd_class, buffer->cmd_select); 178 + return 0; 179 + } 180 + dev_dbg(d, "missing capability %d for %u/%u\n", 181 + call_whitelist[i].need_capability, 182 + buffer->cmd_class, buffer->cmd_select); 183 + 184 + } 185 + 186 + /* not in a whitelist, only allow processes with capabilities */ 187 + if (capable(CAP_SYS_RAWIO)) { 188 + dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n", 189 + buffer->cmd_class, buffer->cmd_select); 190 + return 0; 191 + } 192 + 193 + return -EACCES; 194 + } 195 + EXPORT_SYMBOL_GPL(dell_smbios_call_filter); 196 + 197 + int dell_smbios_call(struct calling_interface_buffer *buffer) 165 198 { 166 - struct smi_cmd command; 199 + int (*call_fn)(struct calling_interface_buffer *) = NULL; 200 + struct device *selected_dev = NULL; 201 + struct smbios_device *priv; 202 + int ret; 167 203 168 - command.magic = SMI_CMD_MAGIC; 169 - command.command_address = da_command_address; 170 - command.command_code = da_command_code; 171 - command.ebx = virt_to_phys(buffer); 172 - command.ecx = 0x42534931; 204 + mutex_lock(&smbios_mutex); 205 + list_for_each_entry(priv, &smbios_device_list, list) { 206 + if (!selected_dev || priv->device->id >= selected_dev->id) { 207 + dev_dbg(priv->device, "Trying device ID: %d\n", 208 + priv->device->id); 209 + call_fn = priv->call_fn; 210 + selected_dev = priv->device; 211 + } 212 + } 173 213 174 - buffer->class = class; 175 - buffer->select = select; 214 + if (!selected_dev) { 215 + ret = -ENODEV; 216 + pr_err("No dell-smbios drivers are loaded\n"); 217 + goto out_smbios_call; 218 + } 176 219 177 - dcdbas_smi_request(&command); 220 + ret = call_fn(buffer); 221 + 222 + out_smbios_call: 223 + mutex_unlock(&smbios_mutex); 224 + return ret; 178 225 } 179 - EXPORT_SYMBOL_GPL(dell_smbios_send_request); 226 + EXPORT_SYMBOL_GPL(dell_smbios_call); 180 227 181 228 struct calling_interface_token *dell_smbios_find_token(int tokenid) 182 229 { ··· 358 139 if (dm->length < 17) 359 140 return; 360 141 361 - da_command_address = table->cmdIOAddress; 362 - da_command_code = table->cmdIOCode; 142 + da_supported_commands = table->supportedCmds; 363 143 364 144 new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * 365 145 sizeof(struct calling_interface_token), ··· 374 156 da_num_tokens += tokens; 375 157 } 376 158 159 + static void zero_duplicates(struct device *dev) 160 + { 161 + int i, j; 162 + 163 + for (i = 0; i < da_num_tokens; i++) { 164 + if (da_tokens[i].tokenID == 0) 165 + continue; 166 + for (j = i+1; j < da_num_tokens; j++) { 167 + if (da_tokens[j].tokenID == 0) 168 + continue; 169 + if (da_tokens[i].tokenID == da_tokens[j].tokenID) { 170 + dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n", 171 + da_tokens[j].tokenID, 172 + da_tokens[j].location, 173 + da_tokens[j].value); 174 + da_tokens[j].tokenID = 0; 175 + } 176 + } 177 + } 178 + } 179 + 377 180 static void __init find_tokens(const struct dmi_header *dm, void *dummy) 378 181 { 379 182 switch (dm->type) { ··· 408 169 } 409 170 } 410 171 172 + static int match_attribute(struct device *dev, 173 + struct device_attribute *attr) 174 + { 175 + int i; 176 + 177 + for (i = 0; i < da_num_tokens * 2; i++) { 178 + if (!token_attrs[i]) 179 + continue; 180 + if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) 181 + return i/2; 182 + } 183 + dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); 184 + return -EINVAL; 185 + } 186 + 187 + static ssize_t location_show(struct device *dev, 188 + struct device_attribute *attr, char *buf) 189 + { 190 + int i; 191 + 192 + if (!capable(CAP_SYS_ADMIN)) 193 + return -EPERM; 194 + 195 + i = match_attribute(dev, attr); 196 + if (i > 0) 197 + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location); 198 + return 0; 199 + } 200 + 201 + static ssize_t value_show(struct device *dev, 202 + struct device_attribute *attr, char *buf) 203 + { 204 + int i; 205 + 206 + if (!capable(CAP_SYS_ADMIN)) 207 + return -EPERM; 208 + 209 + i = match_attribute(dev, attr); 210 + if (i > 0) 211 + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value); 212 + return 0; 213 + } 214 + 215 + static struct attribute_group smbios_attribute_group = { 216 + .name = "tokens" 217 + }; 218 + 219 + static struct platform_driver platform_driver = { 220 + .driver = { 221 + .name = "dell-smbios", 222 + }, 223 + }; 224 + 225 + static int build_tokens_sysfs(struct platform_device *dev) 226 + { 227 + char *location_name; 228 + char *value_name; 229 + size_t size; 230 + int ret; 231 + int i, j; 232 + 233 + /* (number of tokens + 1 for null terminated */ 234 + size = sizeof(struct device_attribute) * (da_num_tokens + 1); 235 + token_location_attrs = kzalloc(size, GFP_KERNEL); 236 + if (!token_location_attrs) 237 + return -ENOMEM; 238 + token_value_attrs = kzalloc(size, GFP_KERNEL); 239 + if (!token_value_attrs) 240 + goto out_allocate_value; 241 + 242 + /* need to store both location and value + terminator*/ 243 + size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); 244 + token_attrs = kzalloc(size, GFP_KERNEL); 245 + if (!token_attrs) 246 + goto out_allocate_attrs; 247 + 248 + for (i = 0, j = 0; i < da_num_tokens; i++) { 249 + /* skip empty */ 250 + if (da_tokens[i].tokenID == 0) 251 + continue; 252 + /* add location */ 253 + location_name = kasprintf(GFP_KERNEL, "%04x_location", 254 + da_tokens[i].tokenID); 255 + if (location_name == NULL) 256 + goto out_unwind_strings; 257 + sysfs_attr_init(&token_location_attrs[i].attr); 258 + token_location_attrs[i].attr.name = location_name; 259 + token_location_attrs[i].attr.mode = 0444; 260 + token_location_attrs[i].show = location_show; 261 + token_attrs[j++] = &token_location_attrs[i].attr; 262 + 263 + /* add value */ 264 + value_name = kasprintf(GFP_KERNEL, "%04x_value", 265 + da_tokens[i].tokenID); 266 + if (value_name == NULL) 267 + goto loop_fail_create_value; 268 + sysfs_attr_init(&token_value_attrs[i].attr); 269 + token_value_attrs[i].attr.name = value_name; 270 + token_value_attrs[i].attr.mode = 0444; 271 + token_value_attrs[i].show = value_show; 272 + token_attrs[j++] = &token_value_attrs[i].attr; 273 + continue; 274 + 275 + loop_fail_create_value: 276 + kfree(value_name); 277 + goto out_unwind_strings; 278 + } 279 + smbios_attribute_group.attrs = token_attrs; 280 + 281 + ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group); 282 + if (ret) 283 + goto out_unwind_strings; 284 + return 0; 285 + 286 + out_unwind_strings: 287 + for (i = i-1; i > 0; i--) { 288 + kfree(token_location_attrs[i].attr.name); 289 + kfree(token_value_attrs[i].attr.name); 290 + } 291 + kfree(token_attrs); 292 + out_allocate_attrs: 293 + kfree(token_value_attrs); 294 + out_allocate_value: 295 + kfree(token_location_attrs); 296 + 297 + return -ENOMEM; 298 + } 299 + 300 + static void free_group(struct platform_device *pdev) 301 + { 302 + int i; 303 + 304 + sysfs_remove_group(&pdev->dev.kobj, 305 + &smbios_attribute_group); 306 + for (i = 0; i < da_num_tokens; i++) { 307 + kfree(token_location_attrs[i].attr.name); 308 + kfree(token_value_attrs[i].attr.name); 309 + } 310 + kfree(token_attrs); 311 + kfree(token_value_attrs); 312 + kfree(token_location_attrs); 313 + } 314 + 411 315 static int __init dell_smbios_init(void) 412 316 { 317 + const struct dmi_device *valid; 413 318 int ret; 319 + 320 + valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL); 321 + if (!valid) { 322 + pr_err("Unable to run on non-Dell system\n"); 323 + return -ENODEV; 324 + } 414 325 415 326 dmi_walk(find_tokens, NULL); 416 327 ··· 569 180 return -ENODEV; 570 181 } 571 182 572 - /* 573 - * Allocate buffer below 4GB for SMI data--only 32-bit physical addr 574 - * is passed to SMI handler. 575 - */ 576 - buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); 577 - if (!buffer) { 183 + ret = platform_driver_register(&platform_driver); 184 + if (ret) 185 + goto fail_platform_driver; 186 + 187 + platform_device = platform_device_alloc("dell-smbios", 0); 188 + if (!platform_device) { 578 189 ret = -ENOMEM; 579 - goto fail_buffer; 190 + goto fail_platform_device_alloc; 580 191 } 192 + ret = platform_device_add(platform_device); 193 + if (ret) 194 + goto fail_platform_device_add; 195 + 196 + /* duplicate tokens will cause problems building sysfs files */ 197 + zero_duplicates(&platform_device->dev); 198 + 199 + ret = build_tokens_sysfs(platform_device); 200 + if (ret) 201 + goto fail_create_group; 581 202 582 203 return 0; 583 204 584 - fail_buffer: 205 + fail_create_group: 206 + platform_device_del(platform_device); 207 + 208 + fail_platform_device_add: 209 + platform_device_put(platform_device); 210 + 211 + fail_platform_device_alloc: 212 + platform_driver_unregister(&platform_driver); 213 + 214 + fail_platform_driver: 585 215 kfree(da_tokens); 586 216 return ret; 587 217 } 588 218 589 219 static void __exit dell_smbios_exit(void) 590 220 { 221 + mutex_lock(&smbios_mutex); 222 + if (platform_device) { 223 + free_group(platform_device); 224 + platform_device_unregister(platform_device); 225 + platform_driver_unregister(&platform_driver); 226 + } 591 227 kfree(da_tokens); 592 - free_page((unsigned long)buffer); 228 + mutex_unlock(&smbios_mutex); 593 229 } 594 230 595 231 subsys_initcall(dell_smbios_init);
+36 -15
drivers/platform/x86/dell-smbios.h
··· 16 16 #ifndef _DELL_SMBIOS_H_ 17 17 #define _DELL_SMBIOS_H_ 18 18 19 + #include <linux/device.h> 20 + #include <uapi/linux/wmi.h> 21 + 22 + /* Classes and selects used only in kernel drivers */ 23 + #define CLASS_KBD_BACKLIGHT 4 24 + #define SELECT_KBD_BACKLIGHT 11 25 + 26 + /* Tokens used in kernel drivers, any of these 27 + * should be filtered from userspace access 28 + */ 29 + #define BRIGHTNESS_TOKEN 0x007d 30 + #define KBD_LED_AC_TOKEN 0x0451 31 + #define KBD_LED_OFF_TOKEN 0x01E1 32 + #define KBD_LED_ON_TOKEN 0x01E2 33 + #define KBD_LED_AUTO_TOKEN 0x01E3 34 + #define KBD_LED_AUTO_25_TOKEN 0x02EA 35 + #define KBD_LED_AUTO_50_TOKEN 0x02EB 36 + #define KBD_LED_AUTO_75_TOKEN 0x02EC 37 + #define KBD_LED_AUTO_100_TOKEN 0x02F6 38 + #define GLOBAL_MIC_MUTE_ENABLE 0x0364 39 + #define GLOBAL_MIC_MUTE_DISABLE 0x0365 40 + 19 41 struct notifier_block; 20 - 21 - /* This structure will be modified by the firmware when we enter 22 - * system management mode, hence the volatiles */ 23 - 24 - struct calling_interface_buffer { 25 - u16 class; 26 - u16 select; 27 - volatile u32 input[4]; 28 - volatile u32 output[4]; 29 - } __packed; 30 42 31 43 struct calling_interface_token { 32 44 u16 tokenID; ··· 49 37 }; 50 38 }; 51 39 52 - int dell_smbios_error(int value); 40 + struct calling_interface_structure { 41 + struct dmi_header header; 42 + u16 cmdIOAddress; 43 + u8 cmdIOCode; 44 + u32 supportedCmds; 45 + struct calling_interface_token tokens[]; 46 + } __packed; 53 47 54 - struct calling_interface_buffer *dell_smbios_get_buffer(void); 55 - void dell_smbios_clear_buffer(void); 56 - void dell_smbios_release_buffer(void); 57 - void dell_smbios_send_request(int class, int select); 48 + int dell_smbios_register_device(struct device *d, void *call_fn); 49 + void dell_smbios_unregister_device(struct device *d); 50 + 51 + int dell_smbios_error(int value); 52 + int dell_smbios_call_filter(struct device *d, 53 + struct calling_interface_buffer *buffer); 54 + int dell_smbios_call(struct calling_interface_buffer *buffer); 58 55 59 56 struct calling_interface_token *dell_smbios_find_token(int tokenid); 60 57
+1 -2
drivers/platform/x86/dell-smo8800.c
··· 90 90 struct smo8800_device, miscdev); 91 91 92 92 u32 data = 0; 93 - unsigned char byte_data = 0; 93 + unsigned char byte_data; 94 94 ssize_t retval = 1; 95 95 96 96 if (count < 1) ··· 103 103 if (retval) 104 104 return retval; 105 105 106 - byte_data = 1; 107 106 retval = 1; 108 107 109 108 if (data < 255)
+191
drivers/platform/x86/dell-wmi-descriptor.c
··· 1 + /* 2 + * Dell WMI descriptor driver 3 + * 4 + * Copyright (C) 2017 Dell Inc. All Rights Reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License version 2 as published 8 + * by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + */ 15 + 16 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 17 + 18 + #include <linux/acpi.h> 19 + #include <linux/list.h> 20 + #include <linux/module.h> 21 + #include <linux/wmi.h> 22 + #include "dell-wmi-descriptor.h" 23 + 24 + #define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" 25 + 26 + struct descriptor_priv { 27 + struct list_head list; 28 + u32 interface_version; 29 + u32 size; 30 + }; 31 + static int descriptor_valid = -EPROBE_DEFER; 32 + static LIST_HEAD(wmi_list); 33 + static DEFINE_MUTEX(list_mutex); 34 + 35 + int dell_wmi_get_descriptor_valid(void) 36 + { 37 + if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID)) 38 + return -ENODEV; 39 + 40 + return descriptor_valid; 41 + } 42 + EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid); 43 + 44 + bool dell_wmi_get_interface_version(u32 *version) 45 + { 46 + struct descriptor_priv *priv; 47 + bool ret = false; 48 + 49 + mutex_lock(&list_mutex); 50 + priv = list_first_entry_or_null(&wmi_list, 51 + struct descriptor_priv, 52 + list); 53 + if (priv) { 54 + *version = priv->interface_version; 55 + ret = true; 56 + } 57 + mutex_unlock(&list_mutex); 58 + return ret; 59 + } 60 + EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version); 61 + 62 + bool dell_wmi_get_size(u32 *size) 63 + { 64 + struct descriptor_priv *priv; 65 + bool ret = false; 66 + 67 + mutex_lock(&list_mutex); 68 + priv = list_first_entry_or_null(&wmi_list, 69 + struct descriptor_priv, 70 + list); 71 + if (priv) { 72 + *size = priv->size; 73 + ret = true; 74 + } 75 + mutex_unlock(&list_mutex); 76 + return ret; 77 + } 78 + EXPORT_SYMBOL_GPL(dell_wmi_get_size); 79 + 80 + /* 81 + * Descriptor buffer is 128 byte long and contains: 82 + * 83 + * Name Offset Length Value 84 + * Vendor Signature 0 4 "DELL" 85 + * Object Signature 4 4 " WMI" 86 + * WMI Interface Version 8 4 <version> 87 + * WMI buffer length 12 4 <length> 88 + */ 89 + static int dell_wmi_descriptor_probe(struct wmi_device *wdev) 90 + { 91 + union acpi_object *obj = NULL; 92 + struct descriptor_priv *priv; 93 + u32 *buffer; 94 + int ret; 95 + 96 + obj = wmidev_block_query(wdev, 0); 97 + if (!obj) { 98 + dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); 99 + ret = -EIO; 100 + goto out; 101 + } 102 + 103 + if (obj->type != ACPI_TYPE_BUFFER) { 104 + dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); 105 + ret = -EINVAL; 106 + descriptor_valid = ret; 107 + goto out; 108 + } 109 + 110 + /* Although it's not technically a failure, this would lead to 111 + * unexpected behavior 112 + */ 113 + if (obj->buffer.length != 128) { 114 + dev_err(&wdev->dev, 115 + "Dell descriptor buffer has unexpected length (%d)\n", 116 + obj->buffer.length); 117 + ret = -EINVAL; 118 + descriptor_valid = ret; 119 + goto out; 120 + } 121 + 122 + buffer = (u32 *)obj->buffer.pointer; 123 + 124 + if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) { 125 + dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n", 126 + buffer); 127 + ret = -EINVAL; 128 + descriptor_valid = ret; 129 + goto out; 130 + } 131 + descriptor_valid = 0; 132 + 133 + if (buffer[2] != 0 && buffer[2] != 1) 134 + dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n", 135 + (unsigned long) buffer[2]); 136 + 137 + priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv), 138 + GFP_KERNEL); 139 + 140 + if (!priv) { 141 + ret = -ENOMEM; 142 + goto out; 143 + } 144 + 145 + priv->interface_version = buffer[2]; 146 + priv->size = buffer[3]; 147 + ret = 0; 148 + dev_set_drvdata(&wdev->dev, priv); 149 + mutex_lock(&list_mutex); 150 + list_add_tail(&priv->list, &wmi_list); 151 + mutex_unlock(&list_mutex); 152 + 153 + dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu and buffer size %lu\n", 154 + (unsigned long) priv->interface_version, 155 + (unsigned long) priv->size); 156 + 157 + out: 158 + kfree(obj); 159 + return ret; 160 + } 161 + 162 + static int dell_wmi_descriptor_remove(struct wmi_device *wdev) 163 + { 164 + struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev); 165 + 166 + mutex_lock(&list_mutex); 167 + list_del(&priv->list); 168 + mutex_unlock(&list_mutex); 169 + return 0; 170 + } 171 + 172 + static const struct wmi_device_id dell_wmi_descriptor_id_table[] = { 173 + { .guid_string = DELL_WMI_DESCRIPTOR_GUID }, 174 + { }, 175 + }; 176 + 177 + static struct wmi_driver dell_wmi_descriptor_driver = { 178 + .driver = { 179 + .name = "dell-wmi-descriptor", 180 + }, 181 + .probe = dell_wmi_descriptor_probe, 182 + .remove = dell_wmi_descriptor_remove, 183 + .id_table = dell_wmi_descriptor_id_table, 184 + }; 185 + 186 + module_wmi_driver(dell_wmi_descriptor_driver); 187 + 188 + MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID); 189 + MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 190 + MODULE_DESCRIPTION("Dell WMI descriptor driver"); 191 + MODULE_LICENSE("GPL");
+27
drivers/platform/x86/dell-wmi-descriptor.h
··· 1 + /* 2 + * Dell WMI descriptor driver 3 + * 4 + * Copyright (c) 2017 Dell Inc. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #ifndef _DELL_WMI_DESCRIPTOR_H_ 12 + #define _DELL_WMI_DESCRIPTOR_H_ 13 + 14 + #include <linux/wmi.h> 15 + 16 + /* possible return values: 17 + * -ENODEV: Descriptor GUID missing from WMI bus 18 + * -EPROBE_DEFER: probing for dell-wmi-descriptor not yet run 19 + * 0: valid descriptor, successfully probed 20 + * < 0: invalid descriptor, don't probe dependent devices 21 + */ 22 + int dell_wmi_get_descriptor_valid(void); 23 + 24 + bool dell_wmi_get_interface_version(u32 *version); 25 + bool dell_wmi_get_size(u32 *size); 26 + 27 + #endif
+15 -82
drivers/platform/x86/dell-wmi.c
··· 39 39 #include <linux/wmi.h> 40 40 #include <acpi/video.h> 41 41 #include "dell-smbios.h" 42 + #include "dell-wmi-descriptor.h" 42 43 43 44 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 44 45 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); ··· 47 46 MODULE_LICENSE("GPL"); 48 47 49 48 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" 50 - #define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" 51 49 52 50 static bool wmi_requires_smbios_request; 53 51 54 52 MODULE_ALIAS("wmi:"DELL_EVENT_GUID); 55 - MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID); 56 53 57 54 struct dell_wmi_priv { 58 55 struct input_dev *input_dev; ··· 618 619 } 619 620 620 621 /* 621 - * Descriptor buffer is 128 byte long and contains: 622 - * 623 - * Name Offset Length Value 624 - * Vendor Signature 0 4 "DELL" 625 - * Object Signature 4 4 " WMI" 626 - * WMI Interface Version 8 4 <version> 627 - * WMI buffer length 12 4 4096 628 - */ 629 - static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev) 630 - { 631 - struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); 632 - union acpi_object *obj = NULL; 633 - struct wmi_device *desc_dev; 634 - u32 *buffer; 635 - int ret; 636 - 637 - desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID); 638 - if (!desc_dev) { 639 - dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n"); 640 - return -ENODEV; 641 - } 642 - 643 - obj = wmidev_block_query(desc_dev, 0); 644 - if (!obj) { 645 - dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); 646 - ret = -EIO; 647 - goto out; 648 - } 649 - 650 - if (obj->type != ACPI_TYPE_BUFFER) { 651 - dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); 652 - ret = -EINVAL; 653 - goto out; 654 - } 655 - 656 - if (obj->buffer.length != 128) { 657 - dev_err(&wdev->dev, 658 - "Dell descriptor buffer has invalid length (%d)\n", 659 - obj->buffer.length); 660 - if (obj->buffer.length < 16) { 661 - ret = -EINVAL; 662 - goto out; 663 - } 664 - } 665 - 666 - buffer = (u32 *)obj->buffer.pointer; 667 - 668 - if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720) 669 - dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n", 670 - 8, buffer); 671 - 672 - if (buffer[2] != 0 && buffer[2] != 1) 673 - dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n", 674 - buffer[2]); 675 - 676 - if (buffer[3] != 4096) 677 - dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n", 678 - buffer[3]); 679 - 680 - priv->interface_version = buffer[2]; 681 - ret = 0; 682 - 683 - dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n", 684 - priv->interface_version); 685 - 686 - out: 687 - kfree(obj); 688 - put_device(&desc_dev->dev); 689 - return ret; 690 - } 691 - 692 - /* 693 622 * According to Dell SMBIOS documentation: 694 623 * 695 624 * 17 3 Application Program Registration ··· 638 711 struct calling_interface_buffer *buffer; 639 712 int ret; 640 713 641 - buffer = dell_smbios_get_buffer(); 714 + buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); 715 + buffer->cmd_class = CLASS_INFO; 716 + buffer->cmd_select = SELECT_APP_REGISTRATION; 642 717 buffer->input[0] = 0x10000; 643 718 buffer->input[1] = 0x51534554; 644 719 buffer->input[3] = enable; 645 - dell_smbios_send_request(17, 3); 646 - ret = buffer->output[0]; 647 - dell_smbios_release_buffer(); 720 + ret = dell_smbios_call(buffer); 721 + if (ret == 0) 722 + ret = buffer->output[0]; 723 + kfree(buffer); 648 724 649 725 return dell_smbios_error(ret); 650 726 } ··· 655 725 static int dell_wmi_probe(struct wmi_device *wdev) 656 726 { 657 727 struct dell_wmi_priv *priv; 658 - int err; 728 + int ret; 729 + 730 + ret = dell_wmi_get_descriptor_valid(); 731 + if (ret) 732 + return ret; 659 733 660 734 priv = devm_kzalloc( 661 735 &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); ··· 667 733 return -ENOMEM; 668 734 dev_set_drvdata(&wdev->dev, priv); 669 735 670 - err = dell_wmi_check_descriptor_buffer(wdev); 671 - if (err) 672 - return err; 736 + if (!dell_wmi_get_interface_version(&priv->interface_version)) 737 + return -EPROBE_DEFER; 673 738 674 739 return dell_wmi_input_setup(wdev); 675 740 }
+9 -5
drivers/platform/x86/fujitsu-laptop.c
··· 691 691 692 692 static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) 693 693 { 694 + struct fujitsu_laptop *priv = acpi_driver_data(device); 694 695 struct led_classdev *led; 695 696 int result; 696 697 ··· 725 724 } 726 725 727 726 /* 728 - * BTNI bit 24 seems to indicate the presence of a radio toggle 729 - * button in place of a slide switch, and all such machines appear 730 - * to also have an RF LED. Therefore use bit 24 as an indicator 731 - * that an RF LED is present. 727 + * Some Fujitsu laptops have a radio toggle button in place of a slide 728 + * switch and all such machines appear to also have an RF LED. Based on 729 + * comparing DSDT tables of four Fujitsu Lifebook models (E744, E751, 730 + * S7110, S8420; the first one has a radio toggle button, the other 731 + * three have slide switches), bit 17 of flags_supported (the value 732 + * returned by method S000 of ACPI device FUJ02E3) seems to indicate 733 + * whether given model has a radio toggle button. 732 734 */ 733 - if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { 735 + if (priv->flags_supported & BIT(17)) { 734 736 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); 735 737 if (!led) 736 738 return -ENOMEM;
+1 -1
drivers/platform/x86/hp-wmi.c
··· 297 297 if (state < 0) 298 298 return state; 299 299 300 - return state & 0x1; 300 + return !!(state & mask); 301 301 } 302 302 303 303 static int __init hp_wmi_bios_2008_later(void)
+1
drivers/platform/x86/hp_accel.c
··· 240 240 AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), 241 241 AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), 242 242 AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), 243 + AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), 243 244 AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), 244 245 AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), 245 246 AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
+7
drivers/platform/x86/ideapad-laptop.c
··· 1166 1166 DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"), 1167 1167 }, 1168 1168 }, 1169 + { 1170 + .ident = "Lenovo YOGA 920-13IKB", 1171 + .matches = { 1172 + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 1173 + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"), 1174 + }, 1175 + }, 1169 1176 {} 1170 1177 }; 1171 1178
+18
drivers/platform/x86/intel-hid.c
··· 226 226 return; 227 227 } 228 228 229 + /* 230 + * Needed for suspend to work on some platforms that don't expose 231 + * the 5-button array, but still send notifies with power button 232 + * event code to this device object on power button actions. 233 + * 234 + * Report the power button press; catch and ignore the button release. 235 + */ 236 + if (!priv->array) { 237 + if (event == 0xce) { 238 + input_report_key(priv->input_dev, KEY_POWER, 1); 239 + input_sync(priv->input_dev); 240 + return; 241 + } 242 + 243 + if (event == 0xcf) 244 + return; 245 + } 246 + 229 247 /* 0xC0 is for HID events, other values are for 5 button array */ 230 248 if (event != 0xc0) { 231 249 if (!priv->array ||
+98
drivers/platform/x86/intel-wmi-thunderbolt.c
··· 1 + /* 2 + * WMI Thunderbolt driver 3 + * 4 + * Copyright (C) 2017 Dell Inc. All Rights Reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License version 2 as published 8 + * by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + */ 15 + 16 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 17 + 18 + #include <linux/acpi.h> 19 + #include <linux/device.h> 20 + #include <linux/fs.h> 21 + #include <linux/kernel.h> 22 + #include <linux/module.h> 23 + #include <linux/string.h> 24 + #include <linux/sysfs.h> 25 + #include <linux/types.h> 26 + #include <linux/wmi.h> 27 + 28 + #define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341" 29 + 30 + static ssize_t force_power_store(struct device *dev, 31 + struct device_attribute *attr, 32 + const char *buf, size_t count) 33 + { 34 + struct acpi_buffer input; 35 + acpi_status status; 36 + u8 mode; 37 + 38 + input.length = sizeof(u8); 39 + input.pointer = &mode; 40 + mode = hex_to_bin(buf[0]); 41 + if (mode == 0 || mode == 1) { 42 + status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1, 43 + &input, NULL); 44 + if (ACPI_FAILURE(status)) 45 + return -ENODEV; 46 + } else { 47 + return -EINVAL; 48 + } 49 + return count; 50 + } 51 + 52 + static DEVICE_ATTR_WO(force_power); 53 + 54 + static struct attribute *tbt_attrs[] = { 55 + &dev_attr_force_power.attr, 56 + NULL 57 + }; 58 + 59 + static const struct attribute_group tbt_attribute_group = { 60 + .attrs = tbt_attrs, 61 + }; 62 + 63 + static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev) 64 + { 65 + int ret; 66 + 67 + ret = sysfs_create_group(&wdev->dev.kobj, &tbt_attribute_group); 68 + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); 69 + return ret; 70 + } 71 + 72 + static int intel_wmi_thunderbolt_remove(struct wmi_device *wdev) 73 + { 74 + sysfs_remove_group(&wdev->dev.kobj, &tbt_attribute_group); 75 + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); 76 + return 0; 77 + } 78 + 79 + static const struct wmi_device_id intel_wmi_thunderbolt_id_table[] = { 80 + { .guid_string = INTEL_WMI_THUNDERBOLT_GUID }, 81 + { }, 82 + }; 83 + 84 + static struct wmi_driver intel_wmi_thunderbolt_driver = { 85 + .driver = { 86 + .name = "intel-wmi-thunderbolt", 87 + }, 88 + .probe = intel_wmi_thunderbolt_probe, 89 + .remove = intel_wmi_thunderbolt_remove, 90 + .id_table = intel_wmi_thunderbolt_id_table, 91 + }; 92 + 93 + module_wmi_driver(intel_wmi_thunderbolt_driver); 94 + 95 + MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID); 96 + MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 97 + MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver"); 98 + MODULE_LICENSE("GPL");
+104 -10
drivers/platform/x86/intel_cht_int33fe.c
··· 24 24 #include <linux/i2c.h> 25 25 #include <linux/interrupt.h> 26 26 #include <linux/module.h> 27 + #include <linux/regulator/consumer.h> 27 28 #include <linux/slab.h> 28 29 29 30 #define EXPECTED_PTYPE 4 ··· 35 34 struct i2c_client *pi3usb30532; 36 35 }; 37 36 37 + /* 38 + * Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates 39 + * the max17047 both through the INT33FE ACPI device (it is right there 40 + * in the resources table) as well as through a separate MAX17047 device. 41 + * 42 + * These helpers are used to work around this by checking if an i2c-client 43 + * for the max17047 has already been registered. 44 + */ 45 + static int cht_int33fe_check_for_max17047(struct device *dev, void *data) 46 + { 47 + struct i2c_client **max17047 = data; 48 + struct acpi_device *adev; 49 + const char *hid; 50 + 51 + adev = ACPI_COMPANION(dev); 52 + if (!adev) 53 + return 0; 54 + 55 + hid = acpi_device_hid(adev); 56 + 57 + /* The MAX17047 ACPI node doesn't have an UID, so we don't check that */ 58 + if (strcmp(hid, "MAX17047")) 59 + return 0; 60 + 61 + *max17047 = to_i2c_client(dev); 62 + return 1; 63 + } 64 + 65 + static struct i2c_client *cht_int33fe_find_max17047(void) 66 + { 67 + struct i2c_client *max17047 = NULL; 68 + 69 + i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047); 70 + return max17047; 71 + } 72 + 38 73 static const char * const max17047_suppliers[] = { "bq24190-charger" }; 39 74 40 75 static const struct property_entry max17047_props[] = { 41 76 PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers), 77 + { } 78 + }; 79 + 80 + static const struct property_entry fusb302_props[] = { 81 + PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"), 82 + PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000), 83 + PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000), 84 + PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000), 42 85 { } 43 86 }; 44 87 ··· 91 46 struct device *dev = &client->dev; 92 47 struct i2c_board_info board_info; 93 48 struct cht_int33fe_data *data; 49 + struct i2c_client *max17047; 50 + struct regulator *regulator; 94 51 unsigned long long ptyp; 95 52 acpi_status status; 96 53 int fusb302_irq; 54 + int ret; 97 55 98 56 status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp); 99 57 if (ACPI_FAILURE(status)) { ··· 111 63 if (ptyp != EXPECTED_PTYPE) 112 64 return -ENODEV; 113 65 66 + /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */ 67 + if (!acpi_dev_present("INT34D3", "1", 3)) { 68 + dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n", 69 + EXPECTED_PTYPE); 70 + return -ENODEV; 71 + } 72 + 73 + /* 74 + * We expect the WC PMIC to be paired with a TI bq24292i charger-IC. 75 + * We check for the bq24292i vbus regulator here, this has 2 purposes: 76 + * 1) The bq24292i allows charging with up to 12V, setting the fusb302's 77 + * max-snk voltage to 12V with another charger-IC is not good. 78 + * 2) For the fusb302 driver to get the bq24292i vbus regulator, the 79 + * regulator-map, which is part of the bq24292i regulator_init_data, 80 + * must be registered before the fusb302 is instantiated, otherwise 81 + * it will end up with a dummy-regulator. 82 + * Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data 83 + * which is defined in i2c-cht-wc.c from where the bq24292i i2c-client 84 + * gets instantiated. We use regulator_get_optional here so that we 85 + * don't end up getting a dummy-regulator ourselves. 86 + */ 87 + regulator = regulator_get_optional(dev, "cht_wc_usb_typec_vbus"); 88 + if (IS_ERR(regulator)) { 89 + ret = PTR_ERR(regulator); 90 + return (ret == -ENODEV) ? -EPROBE_DEFER : ret; 91 + } 92 + regulator_put(regulator); 93 + 114 94 /* The FUSB302 uses the irq at index 1 and is the only irq user */ 115 95 fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1); 116 96 if (fusb302_irq < 0) { ··· 151 75 if (!data) 152 76 return -ENOMEM; 153 77 154 - memset(&board_info, 0, sizeof(board_info)); 155 - strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); 156 - board_info.properties = max17047_props; 78 + /* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */ 79 + max17047 = cht_int33fe_find_max17047(); 80 + if (max17047) { 81 + /* Pre-existing i2c-client for the max17047, add device-props */ 82 + ret = device_add_properties(&max17047->dev, max17047_props); 83 + if (ret) 84 + return ret; 85 + /* And re-probe to get the new device-props applied. */ 86 + ret = device_reprobe(&max17047->dev); 87 + if (ret) 88 + dev_warn(dev, "Reprobing max17047 error: %d\n", ret); 89 + } else { 90 + memset(&board_info, 0, sizeof(board_info)); 91 + strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); 92 + board_info.dev_name = "max17047"; 93 + board_info.properties = max17047_props; 94 + data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); 95 + if (!data->max17047) 96 + return -EPROBE_DEFER; /* Wait for i2c-adapter to load */ 97 + } 157 98 158 - data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); 159 - if (!data->max17047) 160 - return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ 161 - 162 99 memset(&board_info, 0, sizeof(board_info)); 163 - strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE); 100 + strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); 101 + board_info.dev_name = "fusb302"; 102 + board_info.properties = fusb302_props; 164 103 board_info.irq = fusb302_irq; 165 104 166 105 data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info); ··· 183 92 goto out_unregister_max17047; 184 93 185 94 memset(&board_info, 0, sizeof(board_info)); 95 + board_info.dev_name = "pi3usb30532"; 186 96 strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE); 187 97 188 98 data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info); ··· 198 106 i2c_unregister_device(data->fusb302); 199 107 200 108 out_unregister_max17047: 201 - i2c_unregister_device(data->max17047); 109 + if (data->max17047) 110 + i2c_unregister_device(data->max17047); 202 111 203 112 return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ 204 113 } ··· 210 117 211 118 i2c_unregister_device(data->pi3usb30532); 212 119 i2c_unregister_device(data->fusb302); 213 - i2c_unregister_device(data->max17047); 120 + if (data->max17047) 121 + i2c_unregister_device(data->max17047); 214 122 215 123 return 0; 216 124 }
+63 -97
drivers/platform/x86/intel_ips.c
··· 10 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 11 * more details. 12 12 * 13 - * You should have received a copy of the GNU General Public License along with 14 - * this program; if not, write to the Free Software Foundation, Inc., 15 - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 16 - * 17 13 * The full GNU General Public License is included in this distribution in 18 14 * the file called "COPYING". 19 15 * ··· 255 259 256 260 /* Per-SKU limits */ 257 261 struct ips_mcp_limits { 258 - int cpu_family; 259 - int cpu_model; /* includes extended model... */ 260 262 int mcp_power_limit; /* mW units */ 261 263 int core_power_limit; 262 264 int mch_power_limit; ··· 289 295 }; 290 296 291 297 struct ips_driver { 292 - struct pci_dev *dev; 293 - void *regmap; 298 + struct device *dev; 299 + void __iomem *regmap; 300 + int irq; 301 + 294 302 struct task_struct *monitor; 295 303 struct task_struct *adjust; 296 304 struct dentry *debug_root; 305 + struct timer_list timer; 297 306 298 307 /* Average CPU core temps (all averages in .01 degrees C for precision) */ 299 308 u16 ctv1_avg_temp; ··· 591 594 return; 592 595 593 596 if (!ips->gpu_turbo_disable()) 594 - dev_err(&ips->dev->dev, "failed to disable graphics turbo\n"); 597 + dev_err(ips->dev, "failed to disable graphics turbo\n"); 595 598 else 596 599 ips->__gpu_turbo_on = false; 597 600 } ··· 646 649 spin_unlock_irqrestore(&ips->turbo_status_lock, flags); 647 650 648 651 if (ret) 649 - dev_info(&ips->dev->dev, 650 - "CPU power or thermal limit exceeded\n"); 652 + dev_info(ips->dev, "CPU power or thermal limit exceeded\n"); 651 653 652 654 return ret; 653 655 } ··· 765 769 struct ips_driver *ips = data; 766 770 unsigned long flags; 767 771 768 - dev_dbg(&ips->dev->dev, "starting ips-adjust thread\n"); 772 + dev_dbg(ips->dev, "starting ips-adjust thread\n"); 769 773 770 774 /* 771 775 * Adjust CPU and GPU clamps every 5s if needed. Doing it more ··· 812 816 schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD)); 813 817 } while (!kthread_should_stop()); 814 818 815 - dev_dbg(&ips->dev->dev, "ips-adjust thread stopped\n"); 819 + dev_dbg(ips->dev, "ips-adjust thread stopped\n"); 816 820 817 821 return 0; 818 822 } ··· 938 942 return avg; 939 943 } 940 944 941 - static void monitor_timeout(unsigned long arg) 945 + static void monitor_timeout(struct timer_list *t) 942 946 { 943 - wake_up_process((struct task_struct *)arg); 947 + struct ips_driver *ips = from_timer(ips, t, timer); 948 + wake_up_process(ips->monitor); 944 949 } 945 950 946 951 /** ··· 958 961 static int ips_monitor(void *data) 959 962 { 960 963 struct ips_driver *ips = data; 961 - struct timer_list timer; 962 964 unsigned long seqno_timestamp, expire, last_msecs, last_sample_period; 963 965 int i; 964 966 u32 *cpu_samples, *mchp_samples, old_cpu_power; ··· 972 976 mchp_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); 973 977 if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples || 974 978 !cpu_samples || !mchp_samples) { 975 - dev_err(&ips->dev->dev, 979 + dev_err(ips->dev, 976 980 "failed to allocate sample array, ips disabled\n"); 977 981 kfree(mcp_samples); 978 982 kfree(ctv1_samples); ··· 1045 1049 schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); 1046 1050 last_sample_period = IPS_SAMPLE_PERIOD; 1047 1051 1048 - setup_deferrable_timer_on_stack(&timer, monitor_timeout, 1049 - (unsigned long)current); 1052 + timer_setup(&ips->timer, monitor_timeout, TIMER_DEFERRABLE); 1050 1053 do { 1051 1054 u32 cpu_val, mch_val; 1052 1055 u16 val; ··· 1092 1097 ITV_ME_SEQNO_SHIFT; 1093 1098 if (cur_seqno == last_seqno && 1094 1099 time_after(jiffies, seqno_timestamp + HZ)) { 1095 - dev_warn(&ips->dev->dev, "ME failed to update for more than 1s, likely hung\n"); 1100 + dev_warn(ips->dev, 1101 + "ME failed to update for more than 1s, likely hung\n"); 1096 1102 } else { 1097 1103 seqno_timestamp = get_jiffies_64(); 1098 1104 last_seqno = cur_seqno; ··· 1103 1107 expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); 1104 1108 1105 1109 __set_current_state(TASK_INTERRUPTIBLE); 1106 - mod_timer(&timer, expire); 1110 + mod_timer(&ips->timer, expire); 1107 1111 schedule(); 1108 1112 1109 1113 /* Calculate actual sample period for power averaging */ ··· 1112 1116 last_sample_period = 1; 1113 1117 } while (!kthread_should_stop()); 1114 1118 1115 - del_timer_sync(&timer); 1116 - destroy_timer_on_stack(&timer); 1119 + del_timer_sync(&ips->timer); 1117 1120 1118 - dev_dbg(&ips->dev->dev, "ips-monitor thread stopped\n"); 1121 + dev_dbg(ips->dev, "ips-monitor thread stopped\n"); 1119 1122 1120 1123 return 0; 1121 1124 } ··· 1123 1128 #define THM_DUMPW(reg) \ 1124 1129 { \ 1125 1130 u16 val = thm_readw(reg); \ 1126 - dev_dbg(&ips->dev->dev, #reg ": 0x%04x\n", val); \ 1131 + dev_dbg(ips->dev, #reg ": 0x%04x\n", val); \ 1127 1132 } 1128 1133 #define THM_DUMPL(reg) \ 1129 1134 { \ 1130 1135 u32 val = thm_readl(reg); \ 1131 - dev_dbg(&ips->dev->dev, #reg ": 0x%08x\n", val); \ 1136 + dev_dbg(ips->dev, #reg ": 0x%08x\n", val); \ 1132 1137 } 1133 1138 #define THM_DUMPQ(reg) \ 1134 1139 { \ 1135 1140 u64 val = thm_readq(reg); \ 1136 - dev_dbg(&ips->dev->dev, #reg ": 0x%016x\n", val); \ 1141 + dev_dbg(ips->dev, #reg ": 0x%016x\n", val); \ 1137 1142 } 1138 1143 1139 1144 static void dump_thermal_info(struct ips_driver *ips) ··· 1141 1146 u16 ptl; 1142 1147 1143 1148 ptl = thm_readw(THM_PTL); 1144 - dev_dbg(&ips->dev->dev, "Processor temp limit: %d\n", ptl); 1149 + dev_dbg(ips->dev, "Processor temp limit: %d\n", ptl); 1145 1150 1146 1151 THM_DUMPW(THM_CTA); 1147 1152 THM_DUMPW(THM_TRC); ··· 1170 1175 if (!tses && !tes) 1171 1176 return IRQ_NONE; 1172 1177 1173 - dev_info(&ips->dev->dev, "TSES: 0x%02x\n", tses); 1174 - dev_info(&ips->dev->dev, "TES: 0x%02x\n", tes); 1178 + dev_info(ips->dev, "TSES: 0x%02x\n", tses); 1179 + dev_info(ips->dev, "TES: 0x%02x\n", tes); 1175 1180 1176 1181 /* STS update from EC? */ 1177 1182 if (tes & 1) { ··· 1209 1214 1210 1215 /* Thermal trip */ 1211 1216 if (tses) { 1212 - dev_warn(&ips->dev->dev, 1213 - "thermal trip occurred, tses: 0x%04x\n", tses); 1217 + dev_warn(ips->dev, "thermal trip occurred, tses: 0x%04x\n", 1218 + tses); 1214 1219 thm_writeb(THM_TSES, tses); 1215 1220 } 1216 1221 ··· 1325 1330 1326 1331 ips->debug_root = debugfs_create_dir("ips", NULL); 1327 1332 if (!ips->debug_root) { 1328 - dev_err(&ips->dev->dev, 1329 - "failed to create debugfs entries: %ld\n", 1333 + dev_err(ips->dev, "failed to create debugfs entries: %ld\n", 1330 1334 PTR_ERR(ips->debug_root)); 1331 1335 return; 1332 1336 } ··· 1339 1345 ips->debug_root, node, 1340 1346 &ips_debugfs_ops); 1341 1347 if (!ent) { 1342 - dev_err(&ips->dev->dev, 1343 - "failed to create debug file: %ld\n", 1348 + dev_err(ips->dev, "failed to create debug file: %ld\n", 1344 1349 PTR_ERR(ent)); 1345 1350 goto err_cleanup; 1346 1351 } ··· 1366 1373 u16 tdp; 1367 1374 1368 1375 if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) { 1369 - dev_info(&ips->dev->dev, "Non-IPS CPU detected.\n"); 1370 - goto out; 1376 + dev_info(ips->dev, "Non-IPS CPU detected.\n"); 1377 + return NULL; 1371 1378 } 1372 1379 1373 1380 rdmsrl(IA32_MISC_ENABLE, misc_en); ··· 1388 1395 else if (strstr(boot_cpu_data.x86_model_id, "CPU U")) 1389 1396 limits = &ips_ulv_limits; 1390 1397 else { 1391 - dev_info(&ips->dev->dev, "No CPUID match found.\n"); 1392 - goto out; 1398 + dev_info(ips->dev, "No CPUID match found.\n"); 1399 + return NULL; 1393 1400 } 1394 1401 1395 1402 rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power); ··· 1397 1404 1398 1405 /* Sanity check TDP against CPU */ 1399 1406 if (limits->core_power_limit != (tdp / 8) * 1000) { 1400 - dev_info(&ips->dev->dev, "CPU TDP doesn't match expected value (found %d, expected %d)\n", 1407 + dev_info(ips->dev, 1408 + "CPU TDP doesn't match expected value (found %d, expected %d)\n", 1401 1409 tdp / 8, limits->core_power_limit / 1000); 1402 1410 limits->core_power_limit = (tdp / 8) * 1000; 1403 1411 } 1404 1412 1405 - out: 1406 1413 return limits; 1407 1414 } 1408 1415 ··· 1452 1459 { 1453 1460 if (!ips->gpu_busy && late_i915_load) { 1454 1461 if (ips_get_i915_syms(ips)) { 1455 - dev_info(&ips->dev->dev, 1462 + dev_info(ips->dev, 1456 1463 "i915 driver attached, reenabling gpu turbo\n"); 1457 1464 ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS); 1458 1465 } ··· 1473 1480 EXPORT_SYMBOL_GPL(ips_link_to_i915_driver); 1474 1481 1475 1482 static const struct pci_device_id ips_id_table[] = { 1476 - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 1477 - PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, 1483 + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, 1478 1484 { 0, } 1479 1485 }; 1480 1486 ··· 1509 1517 if (dmi_check_system(ips_blacklist)) 1510 1518 return -ENODEV; 1511 1519 1512 - ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL); 1520 + ips = devm_kzalloc(&dev->dev, sizeof(*ips), GFP_KERNEL); 1513 1521 if (!ips) 1514 1522 return -ENOMEM; 1515 1523 1516 - pci_set_drvdata(dev, ips); 1517 - ips->dev = dev; 1524 + spin_lock_init(&ips->turbo_status_lock); 1525 + ips->dev = &dev->dev; 1518 1526 1519 1527 ips->limits = ips_detect_cpu(ips); 1520 1528 if (!ips->limits) { 1521 1529 dev_info(&dev->dev, "IPS not supported on this CPU\n"); 1522 - ret = -ENXIO; 1523 - goto error_free; 1530 + return -ENXIO; 1524 1531 } 1525 1532 1526 - spin_lock_init(&ips->turbo_status_lock); 1527 - 1528 - ret = pci_enable_device(dev); 1533 + ret = pcim_enable_device(dev); 1529 1534 if (ret) { 1530 1535 dev_err(&dev->dev, "can't enable PCI device, aborting\n"); 1531 - goto error_free; 1536 + return ret; 1532 1537 } 1533 1538 1534 - if (!pci_resource_start(dev, 0)) { 1535 - dev_err(&dev->dev, "TBAR not assigned, aborting\n"); 1536 - ret = -ENXIO; 1537 - goto error_free; 1538 - } 1539 - 1540 - ret = pci_request_regions(dev, "ips thermal sensor"); 1539 + ret = pcim_iomap_regions(dev, 1 << 0, pci_name(dev)); 1541 1540 if (ret) { 1542 - dev_err(&dev->dev, "thermal resource busy, aborting\n"); 1543 - goto error_free; 1544 - } 1545 - 1546 - 1547 - ips->regmap = ioremap(pci_resource_start(dev, 0), 1548 - pci_resource_len(dev, 0)); 1549 - if (!ips->regmap) { 1550 1541 dev_err(&dev->dev, "failed to map thermal regs, aborting\n"); 1551 - ret = -EBUSY; 1552 - goto error_release; 1542 + return ret; 1553 1543 } 1544 + ips->regmap = pcim_iomap_table(dev)[0]; 1545 + 1546 + pci_set_drvdata(dev, ips); 1554 1547 1555 1548 tse = thm_readb(THM_TSE); 1556 1549 if (tse != TSE_EN) { 1557 1550 dev_err(&dev->dev, "thermal device not enabled (0x%02x), aborting\n", tse); 1558 - ret = -ENXIO; 1559 - goto error_unmap; 1551 + return -ENXIO; 1560 1552 } 1561 1553 1562 1554 trc = thm_readw(THM_TRC); 1563 1555 trc_required_mask = TRC_CORE1_EN | TRC_CORE_PWR | TRC_MCH_EN; 1564 1556 if ((trc & trc_required_mask) != trc_required_mask) { 1565 1557 dev_err(&dev->dev, "thermal reporting for required devices not enabled, aborting\n"); 1566 - ret = -ENXIO; 1567 - goto error_unmap; 1558 + return -ENXIO; 1568 1559 } 1569 1560 1570 1561 if (trc & TRC_CORE2_EN) ··· 1577 1602 rdmsrl(PLATFORM_INFO, platform_info); 1578 1603 if (!(platform_info & PLATFORM_TDP)) { 1579 1604 dev_err(&dev->dev, "platform indicates TDP override unavailable, aborting\n"); 1580 - ret = -ENODEV; 1581 - goto error_unmap; 1605 + return -ENODEV; 1582 1606 } 1583 1607 1584 1608 /* 1585 1609 * IRQ handler for ME interaction 1586 1610 * Note: don't use MSI here as the PCH has bugs. 1587 1611 */ 1588 - pci_disable_msi(dev); 1589 - ret = request_irq(dev->irq, ips_irq_handler, IRQF_SHARED, "ips", 1590 - ips); 1612 + ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); 1613 + if (ret < 0) 1614 + return ret; 1615 + 1616 + ips->irq = pci_irq_vector(dev, 0); 1617 + 1618 + ret = request_irq(ips->irq, ips_irq_handler, IRQF_SHARED, "ips", ips); 1591 1619 if (ret) { 1592 1620 dev_err(&dev->dev, "request irq failed, aborting\n"); 1593 - goto error_unmap; 1621 + return ret; 1594 1622 } 1595 1623 1596 1624 /* Enable aux, hot & critical interrupts */ ··· 1650 1672 error_thread_cleanup: 1651 1673 kthread_stop(ips->adjust); 1652 1674 error_free_irq: 1653 - free_irq(ips->dev->irq, ips); 1654 - error_unmap: 1655 - iounmap(ips->regmap); 1656 - error_release: 1657 - pci_release_regions(dev); 1658 - error_free: 1659 - kfree(ips); 1675 + free_irq(ips->irq, ips); 1676 + pci_free_irq_vectors(dev); 1660 1677 return ret; 1661 1678 } 1662 1679 ··· 1682 1709 wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); 1683 1710 wrmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); 1684 1711 1685 - free_irq(ips->dev->irq, ips); 1712 + free_irq(ips->irq, ips); 1713 + pci_free_irq_vectors(dev); 1686 1714 if (ips->adjust) 1687 1715 kthread_stop(ips->adjust); 1688 1716 if (ips->monitor) 1689 1717 kthread_stop(ips->monitor); 1690 - iounmap(ips->regmap); 1691 - pci_release_regions(dev); 1692 - kfree(ips); 1693 1718 dev_dbg(&dev->dev, "IPS driver removed\n"); 1694 - } 1695 - 1696 - static void ips_shutdown(struct pci_dev *dev) 1697 - { 1698 1719 } 1699 1720 1700 1721 static struct pci_driver ips_pci_driver = { ··· 1696 1729 .id_table = ips_id_table, 1697 1730 .probe = ips_probe, 1698 1731 .remove = ips_remove, 1699 - .shutdown = ips_shutdown, 1700 1732 }; 1701 1733 1702 1734 module_pci_driver(ips_pci_driver);
-4
drivers/platform/x86/intel_ips.h
··· 10 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 11 * more details. 12 12 * 13 - * You should have received a copy of the GNU General Public License along with 14 - * this program; if not, write to the Free Software Foundation, Inc., 15 - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 16 - * 17 13 * The full GNU General Public License is included in this distribution in 18 14 * the file called "COPYING". 19 15 */
+4 -4
drivers/platform/x86/intel_punit_ipc.c
··· 252 252 * - GTDRIVER_IPC BASE_IFACE 253 253 */ 254 254 res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 255 - if (res) { 255 + if (res && resource_size(res) > 1) { 256 256 addr = devm_ioremap_resource(&pdev->dev, res); 257 257 if (!IS_ERR(addr)) 258 258 punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr; 259 259 } 260 260 261 261 res = platform_get_resource(pdev, IORESOURCE_MEM, 3); 262 - if (res) { 262 + if (res && resource_size(res) > 1) { 263 263 addr = devm_ioremap_resource(&pdev->dev, res); 264 264 if (!IS_ERR(addr)) 265 265 punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr; 266 266 } 267 267 268 268 res = platform_get_resource(pdev, IORESOURCE_MEM, 4); 269 - if (res) { 269 + if (res && resource_size(res) > 1) { 270 270 addr = devm_ioremap_resource(&pdev->dev, res); 271 271 if (!IS_ERR(addr)) 272 272 punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr; 273 273 } 274 274 275 275 res = platform_get_resource(pdev, IORESOURCE_MEM, 5); 276 - if (res) { 276 + if (res && resource_size(res) > 1) { 277 277 addr = devm_ioremap_resource(&pdev->dev, res); 278 278 if (!IS_ERR(addr)) 279 279 punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
+1 -2
drivers/platform/x86/intel_telemetry_core.c
··· 15 15 * Telemetry Framework provides platform related PM and performance statistics. 16 16 * This file provides the core telemetry API implementation. 17 17 */ 18 - #include <linux/module.h> 19 - #include <linux/init.h> 20 18 #include <linux/device.h> 19 + #include <linux/module.h> 21 20 22 21 #include <asm/intel_telemetry.h> 23 22
+10 -14
drivers/platform/x86/intel_telemetry_debugfs.c
··· 21 21 * /sys/kernel/debug/telemetry/ioss_race_verbosity: Write and Change Tracing 22 22 * Verbosity via firmware 23 23 */ 24 - #include <linux/module.h> 25 - #include <linux/init.h> 26 - #include <linux/device.h> 27 24 #include <linux/debugfs.h> 28 - #include <linux/seq_file.h> 25 + #include <linux/device.h> 29 26 #include <linux/io.h> 30 - #include <linux/uaccess.h> 27 + #include <linux/module.h> 31 28 #include <linux/pci.h> 29 + #include <linux/seq_file.h> 32 30 #include <linux/suspend.h> 33 31 34 32 #include <asm/cpu_device_id.h> ··· 73 75 #define TELEM_PSS_LTR_BLOCKING_EVTS 20 74 76 #define TELEM_IOSS_DX_D0IX_EVTS 25 75 77 #define TELEM_IOSS_PG_EVTS 30 76 - 77 - #define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0])) 78 78 79 79 #define TELEM_DEBUGFS_CPU(model, data) \ 80 80 { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data} ··· 300 304 .ioss_d0ix_data = telem_apl_ioss_d0ix_data, 301 305 .ioss_pg_data = telem_apl_ioss_pg_data, 302 306 303 - .pss_idle_evts = TELEM_EVT_LEN(telem_apl_pss_idle_data), 304 - .pcs_idle_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_idle_blkd_data), 305 - .pcs_s0ix_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_s0ix_blkd_data), 306 - .pss_ltr_evts = TELEM_EVT_LEN(telem_apl_pss_ltr_data), 307 - .pss_wakeup_evts = TELEM_EVT_LEN(telem_apl_pss_wakeup), 308 - .ioss_d0ix_evts = TELEM_EVT_LEN(telem_apl_ioss_d0ix_data), 309 - .ioss_pg_evts = TELEM_EVT_LEN(telem_apl_ioss_pg_data), 307 + .pss_idle_evts = ARRAY_SIZE(telem_apl_pss_idle_data), 308 + .pcs_idle_blkd_evts = ARRAY_SIZE(telem_apl_pcs_idle_blkd_data), 309 + .pcs_s0ix_blkd_evts = ARRAY_SIZE(telem_apl_pcs_s0ix_blkd_data), 310 + .pss_ltr_evts = ARRAY_SIZE(telem_apl_pss_ltr_data), 311 + .pss_wakeup_evts = ARRAY_SIZE(telem_apl_pss_wakeup), 312 + .ioss_d0ix_evts = ARRAY_SIZE(telem_apl_ioss_d0ix_data), 313 + .ioss_pg_evts = ARRAY_SIZE(telem_apl_ioss_pg_data), 310 314 311 315 .pstates_id = TELEM_APL_PSS_PSTATES_ID, 312 316 .pss_idle_id = TELEM_APL_PSS_IDLE_ID,
+9 -16
drivers/platform/x86/intel_telemetry_pltdrv.c
··· 16 16 * It used the PUNIT and PMC IPC interfaces for configuring the counters. 17 17 * The accumulated results are fetched from SRAM. 18 18 */ 19 - #include <linux/module.h> 20 - #include <linux/init.h> 21 - #include <linux/device.h> 22 - #include <linux/debugfs.h> 23 - #include <linux/seq_file.h> 19 + 24 20 #include <linux/io.h> 25 - #include <linux/uaccess.h> 26 - #include <linux/pci.h> 27 - #include <linux/suspend.h> 21 + #include <linux/module.h> 28 22 #include <linux/platform_device.h> 29 23 30 24 #include <asm/cpu_device_id.h> ··· 250 256 break; 251 257 252 258 default: 253 - pr_err("Unknown Telemetry action Specified %d\n", action); 259 + pr_err("Unknown Telemetry action specified %d\n", action); 254 260 return -EINVAL; 255 261 } 256 262 ··· 653 659 ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 654 660 TELEM_RESET); 655 661 if (ret) { 656 - dev_err(&pdev->dev, "TELEMTRY Setup Failed\n"); 662 + dev_err(&pdev->dev, "TELEMETRY Setup Failed\n"); 657 663 return ret; 658 664 } 659 665 return 0; ··· 679 685 ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 680 686 TELEM_UPDATE); 681 687 if (ret) 682 - pr_err("TELEMTRY Config Failed\n"); 688 + pr_err("TELEMETRY Config Failed\n"); 683 689 684 690 return ret; 685 691 } ··· 816 822 ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 817 823 TELEM_RESET); 818 824 if (ret) 819 - pr_err("TELEMTRY Reset Failed\n"); 825 + pr_err("TELEMETRY Reset Failed\n"); 820 826 821 827 return ret; 822 828 } ··· 879 885 ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, 880 886 TELEM_ADD); 881 887 if (ret) 882 - pr_err("TELEMTRY ADD Failed\n"); 888 + pr_err("TELEMETRY ADD Failed\n"); 883 889 884 890 return ret; 885 891 } ··· 1189 1195 1190 1196 ret = telemetry_set_pltdata(&telm_pltops, telm_conf); 1191 1197 if (ret) { 1192 - dev_err(&pdev->dev, "TELEMTRY Set Pltops Failed.\n"); 1198 + dev_err(&pdev->dev, "TELEMETRY Set Pltops Failed.\n"); 1193 1199 goto out; 1194 1200 } 1195 1201 ··· 1204 1210 iounmap(telm_conf->pss_config.regmap); 1205 1211 if (telm_conf->ioss_config.regmap) 1206 1212 iounmap(telm_conf->ioss_config.regmap); 1207 - dev_err(&pdev->dev, "TELEMTRY Setup Failed.\n"); 1213 + dev_err(&pdev->dev, "TELEMETRY Setup Failed.\n"); 1208 1214 1209 1215 return ret; 1210 1216 } ··· 1228 1234 1229 1235 static int __init telemetry_module_init(void) 1230 1236 { 1231 - pr_info(DRIVER_NAME ": version %s loaded\n", DRIVER_VERSION); 1232 1237 return platform_driver_register(&telemetry_soc_driver); 1233 1238 } 1234 1239
+1
drivers/platform/x86/intel_turbo_max_3.c
··· 125 125 126 126 static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { 127 127 ICPU(INTEL_FAM6_BROADWELL_X), 128 + ICPU(INTEL_FAM6_SKYLAKE_X), 128 129 {} 129 130 }; 130 131
+2 -2
drivers/platform/x86/mlx-platform.c
··· 216 216 [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), 217 217 }; 218 218 219 - struct platform_device *mlxplat_dev; 220 - struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; 219 + static struct platform_device *mlxplat_dev; 220 + static struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; 221 221 222 222 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) 223 223 {
+16 -3
drivers/platform/x86/peaq-wmi.c
··· 8 8 */ 9 9 10 10 #include <linux/acpi.h> 11 + #include <linux/dmi.h> 11 12 #include <linux/input-polldev.h> 12 13 #include <linux/kernel.h> 13 14 #include <linux/module.h> ··· 65 64 } 66 65 } 67 66 67 + /* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ 68 + static const struct dmi_system_id peaq_dmi_table[] __initconst = { 69 + { 70 + .matches = { 71 + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), 72 + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), 73 + }, 74 + }, 75 + {} 76 + }; 77 + 68 78 static int __init peaq_wmi_init(void) 69 79 { 80 + /* WMI GUID is not unique, also check for a DMI match */ 81 + if (!dmi_check_system(peaq_dmi_table)) 82 + return -ENODEV; 83 + 70 84 if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) 71 85 return -ENODEV; 72 86 ··· 102 86 103 87 static void __exit peaq_wmi_exit(void) 104 88 { 105 - if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) 106 - return; 107 - 108 89 input_unregister_polled_device(peaq_poll_dev); 109 90 } 110 91
+52
drivers/platform/x86/silead_dmi.c
··· 58 58 PROPERTY_ENTRY_U32("touchscreen-size-y", 630), 59 59 PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"), 60 60 PROPERTY_ENTRY_U32("silead,max-fingers", 10), 61 + PROPERTY_ENTRY_BOOL("silead,home-button"), 61 62 { } 62 63 }; 63 64 ··· 73 72 PROPERTY_ENTRY_STRING("firmware-name", 74 73 "gsl1686-surftab-wintron70-st70416-6.fw"), 75 74 PROPERTY_ENTRY_U32("silead,max-fingers", 10), 75 + PROPERTY_ENTRY_BOOL("silead,home-button"), 76 76 { } 77 77 }; 78 78 ··· 85 83 static const struct property_entry gp_electronic_t701_props[] = { 86 84 PROPERTY_ENTRY_U32("touchscreen-size-x", 960), 87 85 PROPERTY_ENTRY_U32("touchscreen-size-y", 640), 86 + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), 87 + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), 88 88 PROPERTY_ENTRY_STRING("firmware-name", 89 89 "gsl1680-gp-electronic-t701.fw"), 90 90 { } ··· 118 114 PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 119 115 PROPERTY_ENTRY_STRING("firmware-name", 120 116 "gsl3692-pov-mobii-wintab-p800w.fw"), 117 + PROPERTY_ENTRY_BOOL("silead,home-button"), 121 118 { } 122 119 }; 123 120 ··· 139 134 static const struct silead_ts_dmi_data itworks_tw891_data = { 140 135 .acpi_name = "MSSL1680:00", 141 136 .properties = itworks_tw891_props, 137 + }; 138 + 139 + static const struct property_entry chuwi_hi8_pro_props[] = { 140 + PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), 141 + PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), 142 + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), 143 + PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), 144 + PROPERTY_ENTRY_BOOL("silead,home-button"), 145 + { } 146 + }; 147 + 148 + static const struct silead_ts_dmi_data chuwi_hi8_pro_data = { 149 + .acpi_name = "MSSL1680:00", 150 + .properties = chuwi_hi8_pro_props, 151 + }; 152 + 153 + static const struct property_entry digma_citi_e200_props[] = { 154 + PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), 155 + PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), 156 + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), 157 + PROPERTY_ENTRY_STRING("firmware-name", 158 + "gsl1686-digma_citi_e200.fw"), 159 + PROPERTY_ENTRY_U32("silead,max-fingers", 10), 160 + PROPERTY_ENTRY_BOOL("silead,home-button"), 161 + { } 162 + }; 163 + 164 + static const struct silead_ts_dmi_data digma_citi_e200_data = { 165 + .acpi_name = "MSSL1680:00", 166 + .properties = digma_citi_e200_props, 142 167 }; 143 168 144 169 static const struct dmi_system_id silead_ts_dmi_table[] = { ··· 252 217 .matches = { 253 218 DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), 254 219 DMI_MATCH(DMI_PRODUCT_NAME, "TW891"), 220 + }, 221 + }, 222 + { 223 + /* Chuwi Hi8 Pro */ 224 + .driver_data = (void *)&chuwi_hi8_pro_data, 225 + .matches = { 226 + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), 227 + DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"), 228 + }, 229 + }, 230 + { 231 + /* Digma Citi E200 */ 232 + .driver_data = (void *)&digma_citi_e200_data, 233 + .matches = { 234 + DMI_MATCH(DMI_SYS_VENDOR, "Digma"), 235 + DMI_MATCH(DMI_PRODUCT_NAME, "CITI E200"), 236 + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), 255 237 }, 256 238 }, 257 239 { },
+9 -7
drivers/platform/x86/sony-laptop.c
··· 1627 1627 static int sony_nc_setup_rfkill(struct acpi_device *device, 1628 1628 enum sony_nc_rfkill nc_type) 1629 1629 { 1630 - int err = 0; 1630 + int err; 1631 1631 struct rfkill *rfk; 1632 1632 enum rfkill_type type; 1633 1633 const char *name; ··· 1660 1660 if (!rfk) 1661 1661 return -ENOMEM; 1662 1662 1663 - if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) { 1663 + err = sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); 1664 + if (err < 0) { 1664 1665 rfkill_destroy(rfk); 1665 - return -1; 1666 + return err; 1666 1667 } 1667 1668 hwblock = !(result & 0x1); 1668 1669 1669 - if (sony_call_snc_handle(sony_rfkill_handle, 1670 - sony_rfkill_address[nc_type], 1671 - &result) < 0) { 1670 + err = sony_call_snc_handle(sony_rfkill_handle, 1671 + sony_rfkill_address[nc_type], 1672 + &result); 1673 + if (err < 0) { 1672 1674 rfkill_destroy(rfk); 1673 - return -1; 1675 + return err; 1674 1676 } 1675 1677 swblock = !(result & 0x2); 1676 1678
+119 -13
drivers/platform/x86/thinkpad_acpi.c
··· 310 310 enum { 311 311 TP_HOTKEY_TABLET_NONE = 0, 312 312 TP_HOTKEY_TABLET_USES_MHKG, 313 - /* X1 Yoga 2016, seen on BIOS N1FET44W */ 314 - TP_HOTKEY_TABLET_USES_CMMD, 313 + TP_HOTKEY_TABLET_USES_GMMS, 315 314 } hotkey_tablet; 316 315 u32 kbdlight:1; 317 316 u32 light:1; ··· 2043 2044 2044 2045 /* HKEY.MHKG() return bits */ 2045 2046 #define TP_HOTKEY_TABLET_MASK (1 << 3) 2046 - /* ThinkPad X1 Yoga (2016) */ 2047 - #define TP_EC_CMMD_TABLET_MODE 0x6 2047 + enum { 2048 + TP_ACPI_MULTI_MODE_INVALID = 0, 2049 + TP_ACPI_MULTI_MODE_UNKNOWN = 1 << 0, 2050 + TP_ACPI_MULTI_MODE_LAPTOP = 1 << 1, 2051 + TP_ACPI_MULTI_MODE_TABLET = 1 << 2, 2052 + TP_ACPI_MULTI_MODE_FLAT = 1 << 3, 2053 + TP_ACPI_MULTI_MODE_STAND = 1 << 4, 2054 + TP_ACPI_MULTI_MODE_TENT = 1 << 5, 2055 + TP_ACPI_MULTI_MODE_STAND_TENT = 1 << 6, 2056 + }; 2057 + 2058 + enum { 2059 + /* The following modes are considered tablet mode for the purpose of 2060 + * reporting the status to userspace. i.e. in all these modes it makes 2061 + * sense to disable the laptop input devices such as touchpad and 2062 + * keyboard. 2063 + */ 2064 + TP_ACPI_MULTI_MODE_TABLET_LIKE = TP_ACPI_MULTI_MODE_TABLET | 2065 + TP_ACPI_MULTI_MODE_STAND | 2066 + TP_ACPI_MULTI_MODE_TENT | 2067 + TP_ACPI_MULTI_MODE_STAND_TENT, 2068 + }; 2048 2069 2049 2070 static int hotkey_get_wlsw(void) 2050 2071 { ··· 2085 2066 return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; 2086 2067 } 2087 2068 2069 + static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode) 2070 + { 2071 + int type = (s >> 16) & 0xffff; 2072 + int value = s & 0xffff; 2073 + int mode = TP_ACPI_MULTI_MODE_INVALID; 2074 + int valid_modes = 0; 2075 + 2076 + if (has_tablet_mode) 2077 + *has_tablet_mode = 0; 2078 + 2079 + switch (type) { 2080 + case 1: 2081 + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | 2082 + TP_ACPI_MULTI_MODE_TABLET | 2083 + TP_ACPI_MULTI_MODE_STAND_TENT; 2084 + break; 2085 + case 2: 2086 + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | 2087 + TP_ACPI_MULTI_MODE_FLAT | 2088 + TP_ACPI_MULTI_MODE_TABLET | 2089 + TP_ACPI_MULTI_MODE_STAND | 2090 + TP_ACPI_MULTI_MODE_TENT; 2091 + break; 2092 + case 3: 2093 + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | 2094 + TP_ACPI_MULTI_MODE_FLAT; 2095 + break; 2096 + case 4: 2097 + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | 2098 + TP_ACPI_MULTI_MODE_TABLET | 2099 + TP_ACPI_MULTI_MODE_STAND | 2100 + TP_ACPI_MULTI_MODE_TENT; 2101 + break; 2102 + case 5: 2103 + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | 2104 + TP_ACPI_MULTI_MODE_FLAT | 2105 + TP_ACPI_MULTI_MODE_TABLET | 2106 + TP_ACPI_MULTI_MODE_STAND | 2107 + TP_ACPI_MULTI_MODE_TENT; 2108 + break; 2109 + default: 2110 + pr_err("Unknown multi mode status type %d with value 0x%04X, please report this to %s\n", 2111 + type, value, TPACPI_MAIL); 2112 + return 0; 2113 + } 2114 + 2115 + if (has_tablet_mode && (valid_modes & TP_ACPI_MULTI_MODE_TABLET_LIKE)) 2116 + *has_tablet_mode = 1; 2117 + 2118 + switch (value) { 2119 + case 1: 2120 + mode = TP_ACPI_MULTI_MODE_LAPTOP; 2121 + break; 2122 + case 2: 2123 + mode = TP_ACPI_MULTI_MODE_FLAT; 2124 + break; 2125 + case 3: 2126 + mode = TP_ACPI_MULTI_MODE_TABLET; 2127 + break; 2128 + case 4: 2129 + if (type == 1) 2130 + mode = TP_ACPI_MULTI_MODE_STAND_TENT; 2131 + else 2132 + mode = TP_ACPI_MULTI_MODE_STAND; 2133 + break; 2134 + case 5: 2135 + mode = TP_ACPI_MULTI_MODE_TENT; 2136 + break; 2137 + default: 2138 + if (type == 5 && value == 0xffff) { 2139 + pr_warn("Multi mode status is undetected, assuming laptop\n"); 2140 + return 0; 2141 + } 2142 + } 2143 + 2144 + if (!(mode & valid_modes)) { 2145 + pr_err("Unknown/reserved multi mode value 0x%04X for type %d, please report this to %s\n", 2146 + value, type, TPACPI_MAIL); 2147 + return 0; 2148 + } 2149 + 2150 + return !!(mode & TP_ACPI_MULTI_MODE_TABLET_LIKE); 2151 + } 2152 + 2088 2153 static int hotkey_get_tablet_mode(int *status) 2089 2154 { 2090 2155 int s; ··· 2180 2077 2181 2078 *status = ((s & TP_HOTKEY_TABLET_MASK) != 0); 2182 2079 break; 2183 - case TP_HOTKEY_TABLET_USES_CMMD: 2184 - if (!acpi_evalf(ec_handle, &s, "CMMD", "d")) 2080 + case TP_HOTKEY_TABLET_USES_GMMS: 2081 + if (!acpi_evalf(hkey_handle, &s, "GMMS", "dd", 0)) 2185 2082 return -EIO; 2186 2083 2187 - *status = (s == TP_EC_CMMD_TABLET_MODE); 2084 + *status = hotkey_gmms_get_tablet_mode(s, NULL); 2188 2085 break; 2189 2086 default: 2190 2087 break; ··· 3216 3113 int in_tablet_mode = 0, res; 3217 3114 char *type = NULL; 3218 3115 3219 - if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) { 3116 + if (acpi_evalf(hkey_handle, &res, "GMMS", "qdd", 0)) { 3117 + int has_tablet_mode; 3118 + 3119 + in_tablet_mode = hotkey_gmms_get_tablet_mode(res, 3120 + &has_tablet_mode); 3121 + if (has_tablet_mode) 3122 + tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_GMMS; 3123 + type = "GMMS"; 3124 + } else if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) { 3220 3125 /* For X41t, X60t, X61t Tablets... */ 3221 3126 tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_MHKG; 3222 3127 in_tablet_mode = !!(res & TP_HOTKEY_TABLET_MASK); 3223 3128 type = "MHKG"; 3224 - } else if (acpi_evalf(ec_handle, &res, "CMMD", "qd")) { 3225 - /* For X1 Yoga (2016) */ 3226 - tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_CMMD; 3227 - in_tablet_mode = res == TP_EC_CMMD_TABLET_MODE; 3228 - type = "CMMD"; 3229 3129 } 3230 3130 3231 3131 if (!tp_features.hotkey_tablet)
+223 -33
drivers/platform/x86/wmi.c
··· 33 33 34 34 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 35 35 36 - #include <linux/kernel.h> 37 - #include <linux/init.h> 38 - #include <linux/types.h> 39 - #include <linux/device.h> 40 - #include <linux/list.h> 41 36 #include <linux/acpi.h> 42 - #include <linux/slab.h> 37 + #include <linux/device.h> 38 + #include <linux/init.h> 39 + #include <linux/kernel.h> 40 + #include <linux/list.h> 41 + #include <linux/miscdevice.h> 43 42 #include <linux/module.h> 44 43 #include <linux/platform_device.h> 45 - #include <linux/wmi.h> 44 + #include <linux/slab.h> 45 + #include <linux/types.h> 46 + #include <linux/uaccess.h> 46 47 #include <linux/uuid.h> 48 + #include <linux/wmi.h> 49 + #include <uapi/linux/wmi.h> 47 50 48 51 ACPI_MODULE_NAME("wmi"); 49 52 MODULE_AUTHOR("Carlos Corbacho"); ··· 72 69 struct wmi_device dev; 73 70 struct list_head list; 74 71 struct guid_block gblock; 72 + struct miscdevice char_dev; 73 + struct mutex char_mutex; 75 74 struct acpi_device *acpi_device; 76 75 wmi_notify_handler handler; 77 76 void *handler_data; 77 + u64 req_buf_size; 78 78 79 79 bool read_takes_no_args; 80 80 }; ··· 194 188 /* 195 189 * Exported WMI functions 196 190 */ 191 + 192 + /** 193 + * set_required_buffer_size - Sets the buffer size needed for performing IOCTL 194 + * @wdev: A wmi bus device from a driver 195 + * @instance: Instance index 196 + * 197 + * Allocates memory needed for buffer, stores the buffer size in that memory 198 + */ 199 + int set_required_buffer_size(struct wmi_device *wdev, u64 length) 200 + { 201 + struct wmi_block *wblock; 202 + 203 + wblock = container_of(wdev, struct wmi_block, dev); 204 + wblock->req_buf_size = length; 205 + 206 + return 0; 207 + } 208 + EXPORT_SYMBOL_GPL(set_required_buffer_size); 209 + 197 210 /** 198 211 * wmi_evaluate_method - Evaluate a WMI method 199 212 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba ··· 226 201 acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, 227 202 u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 228 203 { 204 + struct wmi_block *wblock = NULL; 205 + 206 + if (!find_guid(guid_string, &wblock)) 207 + return AE_ERROR; 208 + return wmidev_evaluate_method(&wblock->dev, instance, method_id, 209 + in, out); 210 + } 211 + EXPORT_SYMBOL_GPL(wmi_evaluate_method); 212 + 213 + /** 214 + * wmidev_evaluate_method - Evaluate a WMI method 215 + * @wdev: A wmi bus device from a driver 216 + * @instance: Instance index 217 + * @method_id: Method ID to call 218 + * &in: Buffer containing input for the method call 219 + * &out: Empty buffer to return the method results 220 + * 221 + * Call an ACPI-WMI method 222 + */ 223 + acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, 224 + u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) 225 + { 229 226 struct guid_block *block = NULL; 230 227 struct wmi_block *wblock = NULL; 231 228 acpi_handle handle; ··· 256 209 union acpi_object params[3]; 257 210 char method[5] = "WM"; 258 211 259 - if (!find_guid(guid_string, &wblock)) 260 - return AE_ERROR; 261 - 212 + wblock = container_of(wdev, struct wmi_block, dev); 262 213 block = &wblock->gblock; 263 214 handle = wblock->acpi_device->handle; 264 215 ··· 291 246 292 247 return status; 293 248 } 294 - EXPORT_SYMBOL_GPL(wmi_evaluate_method); 249 + EXPORT_SYMBOL_GPL(wmidev_evaluate_method); 295 250 296 251 static acpi_status __query_block(struct wmi_block *wblock, u8 instance, 297 252 struct acpi_buffer *out) ··· 392 347 return (union acpi_object *)out.pointer; 393 348 } 394 349 EXPORT_SYMBOL_GPL(wmidev_block_query); 395 - 396 - struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, 397 - const char *guid_string) 398 - { 399 - struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev); 400 - struct wmi_block *other_wb; 401 - 402 - if (!find_guid(guid_string, &other_wb)) 403 - return NULL; 404 - 405 - if (other_wb->acpi_device != this_wb->acpi_device) 406 - return NULL; 407 - 408 - get_device(&other_wb->dev.dev); 409 - return &other_wb->dev; 410 - } 411 - EXPORT_SYMBOL_GPL(wmidev_get_other_guid); 412 350 413 351 /** 414 352 * wmi_set_block - Write to a WMI block ··· 789 761 790 762 return 0; 791 763 } 764 + static int wmi_char_open(struct inode *inode, struct file *filp) 765 + { 766 + const char *driver_name = filp->f_path.dentry->d_iname; 767 + struct wmi_block *wblock = NULL; 768 + struct wmi_block *next = NULL; 769 + 770 + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { 771 + if (!wblock->dev.dev.driver) 772 + continue; 773 + if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) { 774 + filp->private_data = wblock; 775 + break; 776 + } 777 + } 778 + 779 + if (!filp->private_data) 780 + return -ENODEV; 781 + 782 + return nonseekable_open(inode, filp); 783 + } 784 + 785 + static ssize_t wmi_char_read(struct file *filp, char __user *buffer, 786 + size_t length, loff_t *offset) 787 + { 788 + struct wmi_block *wblock = filp->private_data; 789 + 790 + return simple_read_from_buffer(buffer, length, offset, 791 + &wblock->req_buf_size, 792 + sizeof(wblock->req_buf_size)); 793 + } 794 + 795 + static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 796 + { 797 + struct wmi_ioctl_buffer __user *input = 798 + (struct wmi_ioctl_buffer __user *) arg; 799 + struct wmi_block *wblock = filp->private_data; 800 + struct wmi_ioctl_buffer *buf = NULL; 801 + struct wmi_driver *wdriver = NULL; 802 + int ret; 803 + 804 + if (_IOC_TYPE(cmd) != WMI_IOC) 805 + return -ENOTTY; 806 + 807 + /* make sure we're not calling a higher instance than exists*/ 808 + if (_IOC_NR(cmd) >= wblock->gblock.instance_count) 809 + return -EINVAL; 810 + 811 + mutex_lock(&wblock->char_mutex); 812 + buf = wblock->handler_data; 813 + if (get_user(buf->length, &input->length)) { 814 + dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); 815 + ret = -EFAULT; 816 + goto out_ioctl; 817 + } 818 + /* if it's too small, abort */ 819 + if (buf->length < wblock->req_buf_size) { 820 + dev_err(&wblock->dev.dev, 821 + "Buffer %lld too small, need at least %lld\n", 822 + buf->length, wblock->req_buf_size); 823 + ret = -EINVAL; 824 + goto out_ioctl; 825 + } 826 + /* if it's too big, warn, driver will only use what is needed */ 827 + if (buf->length > wblock->req_buf_size) 828 + dev_warn(&wblock->dev.dev, 829 + "Buffer %lld is bigger than required %lld\n", 830 + buf->length, wblock->req_buf_size); 831 + 832 + /* copy the structure from userspace */ 833 + if (copy_from_user(buf, input, wblock->req_buf_size)) { 834 + dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", 835 + wblock->req_buf_size); 836 + ret = -EFAULT; 837 + goto out_ioctl; 838 + } 839 + 840 + /* let the driver do any filtering and do the call */ 841 + wdriver = container_of(wblock->dev.dev.driver, 842 + struct wmi_driver, driver); 843 + if (!try_module_get(wdriver->driver.owner)) { 844 + ret = -EBUSY; 845 + goto out_ioctl; 846 + } 847 + ret = wdriver->filter_callback(&wblock->dev, cmd, buf); 848 + module_put(wdriver->driver.owner); 849 + if (ret) 850 + goto out_ioctl; 851 + 852 + /* return the result (only up to our internal buffer size) */ 853 + if (copy_to_user(input, buf, wblock->req_buf_size)) { 854 + dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", 855 + wblock->req_buf_size); 856 + ret = -EFAULT; 857 + } 858 + 859 + out_ioctl: 860 + mutex_unlock(&wblock->char_mutex); 861 + return ret; 862 + } 863 + 864 + static const struct file_operations wmi_fops = { 865 + .owner = THIS_MODULE, 866 + .read = wmi_char_read, 867 + .open = wmi_char_open, 868 + .unlocked_ioctl = wmi_ioctl, 869 + .compat_ioctl = wmi_ioctl, 870 + }; 792 871 793 872 static int wmi_dev_probe(struct device *dev) 794 873 { ··· 903 768 struct wmi_driver *wdriver = 904 769 container_of(dev->driver, struct wmi_driver, driver); 905 770 int ret = 0; 771 + int count; 772 + char *buf; 906 773 907 774 if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) 908 775 dev_warn(dev, "failed to enable device -- probing anyway\n"); 909 776 910 777 if (wdriver->probe) { 911 778 ret = wdriver->probe(dev_to_wdev(dev)); 912 - if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) 913 - dev_warn(dev, "failed to disable device\n"); 779 + if (ret != 0) 780 + goto probe_failure; 914 781 } 915 782 783 + /* driver wants a character device made */ 784 + if (wdriver->filter_callback) { 785 + /* check that required buffer size declared by driver or MOF */ 786 + if (!wblock->req_buf_size) { 787 + dev_err(&wblock->dev.dev, 788 + "Required buffer size not set\n"); 789 + ret = -EINVAL; 790 + goto probe_failure; 791 + } 792 + 793 + count = get_order(wblock->req_buf_size); 794 + wblock->handler_data = (void *)__get_free_pages(GFP_KERNEL, 795 + count); 796 + if (!wblock->handler_data) { 797 + ret = -ENOMEM; 798 + goto probe_failure; 799 + } 800 + 801 + buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL); 802 + if (!buf) { 803 + ret = -ENOMEM; 804 + goto probe_string_failure; 805 + } 806 + sprintf(buf, "wmi/%s", wdriver->driver.name); 807 + wblock->char_dev.minor = MISC_DYNAMIC_MINOR; 808 + wblock->char_dev.name = buf; 809 + wblock->char_dev.fops = &wmi_fops; 810 + wblock->char_dev.mode = 0444; 811 + ret = misc_register(&wblock->char_dev); 812 + if (ret) { 813 + dev_warn(dev, "failed to register char dev: %d", ret); 814 + ret = -ENOMEM; 815 + goto probe_misc_failure; 816 + } 817 + } 818 + 819 + return 0; 820 + 821 + probe_misc_failure: 822 + kfree(buf); 823 + probe_string_failure: 824 + kfree(wblock->handler_data); 825 + probe_failure: 826 + if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) 827 + dev_warn(dev, "failed to disable device\n"); 916 828 return ret; 917 829 } 918 830 ··· 969 787 struct wmi_driver *wdriver = 970 788 container_of(dev->driver, struct wmi_driver, driver); 971 789 int ret = 0; 790 + 791 + if (wdriver->filter_callback) { 792 + misc_deregister(&wblock->char_dev); 793 + kfree(wblock->char_dev.name); 794 + free_pages((unsigned long)wblock->handler_data, 795 + get_order(wblock->req_buf_size)); 796 + } 972 797 973 798 if (wdriver->remove) 974 799 ret = wdriver->remove(dev_to_wdev(dev)); ··· 1033 844 1034 845 if (gblock->flags & ACPI_WMI_METHOD) { 1035 846 wblock->dev.dev.type = &wmi_type_method; 847 + mutex_init(&wblock->char_mutex); 1036 848 goto out_init; 1037 849 } 1038 850 ··· 1335 1145 acpi_remove_address_space_handler(acpi_device->handle, 1336 1146 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 1337 1147 wmi_free_devices(acpi_device); 1338 - device_unregister((struct device *)dev_get_drvdata(&device->dev)); 1148 + device_destroy(&wmi_bus_class, MKDEV(0, 0)); 1339 1149 1340 1150 return 0; 1341 1151 } ··· 1389 1199 return 0; 1390 1200 1391 1201 err_remove_busdev: 1392 - device_unregister(wmi_bus_dev); 1202 + device_destroy(&wmi_bus_class, MKDEV(0, 0)); 1393 1203 1394 1204 err_remove_notify_handler: 1395 1205 acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, ··· 1454 1264 static void __exit acpi_wmi_exit(void) 1455 1265 { 1456 1266 platform_driver_unregister(&acpi_wmi_driver); 1457 - class_unregister(&wmi_bus_class); 1458 1267 bus_unregister(&wmi_bus_type); 1268 + class_unregister(&wmi_bus_class); 1459 1269 } 1460 1270 1461 1271 subsys_initcall(acpi_wmi_init);
+10 -3
include/linux/wmi.h
··· 18 18 19 19 #include <linux/device.h> 20 20 #include <linux/acpi.h> 21 + #include <uapi/linux/wmi.h> 21 22 22 23 struct wmi_device { 23 24 struct device dev; ··· 27 26 bool setable; 28 27 }; 29 28 29 + /* evaluate the ACPI method associated with this device */ 30 + extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, 31 + u8 instance, u32 method_id, 32 + const struct acpi_buffer *in, 33 + struct acpi_buffer *out); 34 + 30 35 /* Caller must kfree the result. */ 31 36 extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, 32 37 u8 instance); 33 38 34 - /* Gets another device on the same bus. Caller must put_device the result. */ 35 - extern struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, 36 - const char *guid_string); 39 + extern int set_required_buffer_size(struct wmi_device *wdev, u64 length); 37 40 38 41 struct wmi_device_id { 39 42 const char *guid_string; ··· 50 45 int (*probe)(struct wmi_device *wdev); 51 46 int (*remove)(struct wmi_device *wdev); 52 47 void (*notify)(struct wmi_device *device, union acpi_object *data); 48 + long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd, 49 + struct wmi_ioctl_buffer *arg); 53 50 }; 54 51 55 52 extern int __must_check __wmi_driver_register(struct wmi_driver *driver,
+73
include/uapi/linux/wmi.h
··· 1 + /* 2 + * User API methods for ACPI-WMI mapping driver 3 + * 4 + * Copyright (C) 2017 Dell, Inc. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + #ifndef _UAPI_LINUX_WMI_H 11 + #define _UAPI_LINUX_WMI_H 12 + 13 + #include <linux/ioctl.h> 14 + #include <linux/types.h> 15 + 16 + /* WMI bus will filter all WMI vendor driver requests through this IOC */ 17 + #define WMI_IOC 'W' 18 + 19 + /* All ioctl requests through WMI should declare their size followed by 20 + * relevant data objects 21 + */ 22 + struct wmi_ioctl_buffer { 23 + __u64 length; 24 + __u8 data[]; 25 + }; 26 + 27 + /* This structure may be modified by the firmware when we enter 28 + * system management mode through SMM, hence the volatiles 29 + */ 30 + struct calling_interface_buffer { 31 + __u16 cmd_class; 32 + __u16 cmd_select; 33 + volatile __u32 input[4]; 34 + volatile __u32 output[4]; 35 + } __packed; 36 + 37 + struct dell_wmi_extensions { 38 + __u32 argattrib; 39 + __u32 blength; 40 + __u8 data[]; 41 + } __packed; 42 + 43 + struct dell_wmi_smbios_buffer { 44 + __u64 length; 45 + struct calling_interface_buffer std; 46 + struct dell_wmi_extensions ext; 47 + } __packed; 48 + 49 + /* Whitelisted smbios class/select commands */ 50 + #define CLASS_TOKEN_READ 0 51 + #define CLASS_TOKEN_WRITE 1 52 + #define SELECT_TOKEN_STD 0 53 + #define SELECT_TOKEN_BAT 1 54 + #define SELECT_TOKEN_AC 2 55 + #define CLASS_FLASH_INTERFACE 7 56 + #define SELECT_FLASH_INTERFACE 3 57 + #define CLASS_ADMIN_PROP 10 58 + #define SELECT_ADMIN_PROP 3 59 + #define CLASS_INFO 17 60 + #define SELECT_RFKILL 11 61 + #define SELECT_APP_REGISTRATION 3 62 + #define SELECT_DOCK 22 63 + 64 + /* whitelisted tokens */ 65 + #define CAPSULE_EN_TOKEN 0x0461 66 + #define CAPSULE_DIS_TOKEN 0x0462 67 + #define WSMT_EN_TOKEN 0x04EC 68 + #define WSMT_DIS_TOKEN 0x04ED 69 + 70 + /* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */ 71 + #define DELL_WMI_SMBIOS_CMD _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer) 72 + 73 + #endif
+8 -6
tools/Makefile
··· 30 30 @echo ' usb - USB testing tools' 31 31 @echo ' virtio - vhost test module' 32 32 @echo ' vm - misc vm tools' 33 + @echo ' wmi - WMI interface examples' 33 34 @echo ' x86_energy_perf_policy - Intel energy policy tool' 34 35 @echo '' 35 36 @echo 'You can do:' ··· 59 58 cpupower: FORCE 60 59 $(call descend,power/$@) 61 60 62 - cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds: FORCE 61 + cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE 63 62 $(call descend,$@) 64 63 65 64 liblockdep: FORCE ··· 94 93 all: acpi cgroup cpupower gpio hv firewire liblockdep \ 95 94 perf selftests spi turbostat usb \ 96 95 virtio vm bpf x86_energy_perf_policy \ 97 - tmon freefall iio objtool kvm_stat 96 + tmon freefall iio objtool kvm_stat wmi 98 97 99 98 acpi_install: 100 99 $(call descend,power/$(@:_install=),install) ··· 102 101 cpupower_install: 103 102 $(call descend,power/$(@:_install=),install) 104 103 105 - cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install: 104 + cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install: 106 105 $(call descend,$(@:_install=),install) 107 106 108 107 liblockdep_install: ··· 127 126 hv_install firewire_install iio_install liblockdep_install \ 128 127 perf_install selftests_install turbostat_install usb_install \ 129 128 virtio_install vm_install bpf_install x86_energy_perf_policy_install \ 130 - tmon_install freefall_install objtool_install kvm_stat_install 129 + tmon_install freefall_install objtool_install kvm_stat_install \ 130 + wmi_install 131 131 132 132 acpi_clean: 133 133 $(call descend,power/acpi,clean) ··· 136 134 cpupower_clean: 137 135 $(call descend,power/cpupower,clean) 138 136 139 - cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean: 137 + cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean: 140 138 $(call descend,$(@:_clean=),clean) 141 139 142 140 liblockdep_clean: ··· 174 172 perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ 175 173 vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ 176 174 freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ 177 - gpio_clean objtool_clean leds_clean 175 + gpio_clean objtool_clean leds_clean wmi_clean 178 176 179 177 .PHONY: FORCE
+18
tools/wmi/Makefile
··· 1 + PREFIX ?= /usr 2 + SBINDIR ?= sbin 3 + INSTALL ?= install 4 + CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include 5 + CC = $(CROSS_COMPILE)gcc 6 + 7 + TARGET = dell-smbios-example 8 + 9 + all: $(TARGET) 10 + 11 + %: %.c 12 + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< 13 + 14 + clean: 15 + $(RM) $(TARGET) 16 + 17 + install: dell-smbios-example 18 + $(INSTALL) -D -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/$(SBINDIR)/$(TARGET)
+210
tools/wmi/dell-smbios-example.c
··· 1 + /* 2 + * Sample application for SMBIOS communication over WMI interface 3 + * Performs the following: 4 + * - Simple cmd_class/cmd_select lookup for TPM information 5 + * - Simple query of known tokens and their values 6 + * - Simple activation of a token 7 + * 8 + * Copyright (C) 2017 Dell, Inc. 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License version 2 as 12 + * published by the Free Software Foundation. 13 + */ 14 + 15 + #include <errno.h> 16 + #include <fcntl.h> 17 + #include <stdio.h> 18 + #include <stdlib.h> 19 + #include <sys/ioctl.h> 20 + #include <unistd.h> 21 + 22 + /* if uapi header isn't installed, this might not yet exist */ 23 + #ifndef __packed 24 + #define __packed __attribute__((packed)) 25 + #endif 26 + #include <linux/wmi.h> 27 + 28 + /* It would be better to discover these using udev, but for a simple 29 + * application they're hardcoded 30 + */ 31 + static const char *ioctl_devfs = "/dev/wmi/dell-smbios"; 32 + static const char *token_sysfs = 33 + "/sys/bus/platform/devices/dell-smbios.0/tokens"; 34 + 35 + static void show_buffer(struct dell_wmi_smbios_buffer *buffer) 36 + { 37 + printf("Call: %x/%x [%x,%x,%x,%x]\nResults: [%8x,%8x,%8x,%8x]\n", 38 + buffer->std.cmd_class, buffer->std.cmd_select, 39 + buffer->std.input[0], buffer->std.input[1], 40 + buffer->std.input[2], buffer->std.input[3], 41 + buffer->std.output[0], buffer->std.output[1], 42 + buffer->std.output[2], buffer->std.output[3]); 43 + } 44 + 45 + static int run_wmi_smbios_cmd(struct dell_wmi_smbios_buffer *buffer) 46 + { 47 + int fd; 48 + int ret; 49 + 50 + fd = open(ioctl_devfs, O_NONBLOCK); 51 + ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer); 52 + close(fd); 53 + return ret; 54 + } 55 + 56 + static int find_token(__u16 token, __u16 *location, __u16 *value) 57 + { 58 + char location_sysfs[60]; 59 + char value_sysfs[57]; 60 + char buf[4096]; 61 + FILE *f; 62 + int ret; 63 + 64 + ret = sprintf(value_sysfs, "%s/%04x_value", token_sysfs, token); 65 + if (ret < 0) { 66 + printf("sprintf value failed\n"); 67 + return 2; 68 + } 69 + f = fopen(value_sysfs, "rb"); 70 + if (!f) { 71 + printf("failed to open %s\n", value_sysfs); 72 + return 2; 73 + } 74 + fread(buf, 1, 4096, f); 75 + fclose(f); 76 + *value = (__u16) strtol(buf, NULL, 16); 77 + 78 + ret = sprintf(location_sysfs, "%s/%04x_location", token_sysfs, token); 79 + if (ret < 0) { 80 + printf("sprintf location failed\n"); 81 + return 1; 82 + } 83 + f = fopen(location_sysfs, "rb"); 84 + if (!f) { 85 + printf("failed to open %s\n", location_sysfs); 86 + return 2; 87 + } 88 + fread(buf, 1, 4096, f); 89 + fclose(f); 90 + *location = (__u16) strtol(buf, NULL, 16); 91 + 92 + if (*location) 93 + return 0; 94 + return 2; 95 + } 96 + 97 + static int token_is_active(__u16 *location, __u16 *cmpvalue, 98 + struct dell_wmi_smbios_buffer *buffer) 99 + { 100 + int ret; 101 + 102 + buffer->std.cmd_class = CLASS_TOKEN_READ; 103 + buffer->std.cmd_select = SELECT_TOKEN_STD; 104 + buffer->std.input[0] = *location; 105 + ret = run_wmi_smbios_cmd(buffer); 106 + if (ret != 0 || buffer->std.output[0] != 0) 107 + return ret; 108 + ret = (buffer->std.output[1] == *cmpvalue); 109 + return ret; 110 + } 111 + 112 + static int query_token(__u16 token, struct dell_wmi_smbios_buffer *buffer) 113 + { 114 + __u16 location; 115 + __u16 value; 116 + int ret; 117 + 118 + ret = find_token(token, &location, &value); 119 + if (ret != 0) { 120 + printf("unable to find token %04x\n", token); 121 + return 1; 122 + } 123 + return token_is_active(&location, &value, buffer); 124 + } 125 + 126 + static int activate_token(struct dell_wmi_smbios_buffer *buffer, 127 + __u16 token) 128 + { 129 + __u16 location; 130 + __u16 value; 131 + int ret; 132 + 133 + ret = find_token(token, &location, &value); 134 + if (ret != 0) { 135 + printf("unable to find token %04x\n", token); 136 + return 1; 137 + } 138 + buffer->std.cmd_class = CLASS_TOKEN_WRITE; 139 + buffer->std.cmd_select = SELECT_TOKEN_STD; 140 + buffer->std.input[0] = location; 141 + buffer->std.input[1] = 1; 142 + ret = run_wmi_smbios_cmd(buffer); 143 + return ret; 144 + } 145 + 146 + static int query_buffer_size(__u64 *buffer_size) 147 + { 148 + FILE *f; 149 + 150 + f = fopen(ioctl_devfs, "rb"); 151 + if (!f) 152 + return -EINVAL; 153 + fread(buffer_size, sizeof(__u64), 1, f); 154 + fclose(f); 155 + return EXIT_SUCCESS; 156 + } 157 + 158 + int main(void) 159 + { 160 + struct dell_wmi_smbios_buffer *buffer; 161 + int ret; 162 + __u64 value = 0; 163 + 164 + ret = query_buffer_size(&value); 165 + if (ret == EXIT_FAILURE || !value) { 166 + printf("Unable to read buffer size\n"); 167 + goto out; 168 + } 169 + printf("Detected required buffer size %lld\n", value); 170 + 171 + buffer = malloc(value); 172 + if (buffer == NULL) { 173 + printf("failed to alloc memory for ioctl\n"); 174 + ret = -ENOMEM; 175 + goto out; 176 + } 177 + buffer->length = value; 178 + 179 + /* simple SMBIOS call for looking up TPM info */ 180 + buffer->std.cmd_class = CLASS_FLASH_INTERFACE; 181 + buffer->std.cmd_select = SELECT_FLASH_INTERFACE; 182 + buffer->std.input[0] = 2; 183 + ret = run_wmi_smbios_cmd(buffer); 184 + if (ret) { 185 + printf("smbios ioctl failed: %d\n", ret); 186 + ret = EXIT_FAILURE; 187 + goto out; 188 + } 189 + show_buffer(buffer); 190 + 191 + /* query some tokens */ 192 + ret = query_token(CAPSULE_EN_TOKEN, buffer); 193 + printf("UEFI Capsule enabled token is: %d\n", ret); 194 + ret = query_token(CAPSULE_DIS_TOKEN, buffer); 195 + printf("UEFI Capsule disabled token is: %d\n", ret); 196 + 197 + /* activate UEFI capsule token if disabled */ 198 + if (ret) { 199 + printf("Enabling UEFI capsule token"); 200 + if (activate_token(buffer, CAPSULE_EN_TOKEN)) { 201 + printf("activate failed\n"); 202 + ret = -1; 203 + goto out; 204 + } 205 + } 206 + ret = EXIT_SUCCESS; 207 + out: 208 + free(buffer); 209 + return ret; 210 + }