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

Configure Feed

Select the types of activity you want to include in your feed.

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

Pull x86 platform driver updates from Hans de Goede:
"Highlights:

- New driver for changing BIOS settings from within Linux on Dell
devices. This introduces a new generic sysfs API for this. Lenovo
is working on also supporting this API on their devices

- New Intel PMT telemetry and crashlog drivers

- Support for SW_TABLET_MODE reporting for the acer-wmi and intel-hid
drivers

- Preparation work for improving support for Microsoft Surface
hardware

- Various fixes / improvements / quirks for the panasonic-laptop and
others"

* tag 'platform-drivers-x86-v5.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (81 commits)
platform/x86: ISST: Mark mmio_range_devid_0 and mmio_range_devid_1 with static keyword
platform/x86: intel-hid: add Rocket Lake ACPI device ID
x86/platform: classmate-laptop: add WiFi media button
platform/x86: mlx-platform: Fix item counter assignment for MSN2700/ComEx system
platform/x86: mlx-platform: Fix item counter assignment for MSN2700, MSN24xx systems
tools/power/x86/intel-speed-select: Update version for v5.11
tools/power/x86/intel-speed-select: Account for missing sysfs for die_id
tools/power/x86/intel-speed-select: Read TRL from mailbox
platform/x86: intel-hid: Do not create SW_TABLET_MODE input-dev when a KIOX010A ACPI dev is present
platform/x86: intel-hid: Add alternative method to enable switches
platform/x86: intel-hid: Add support for SW_TABLET_MODE
platform/x86: intel-vbtn: Fix SW_TABLET_MODE always reporting 1 on some HP x360 models
platform/x86: ISST: Change PCI device macros
platform/x86: ISST: Allow configurable offset range
platform/x86: ISST: Check for unaligned mmio address
acer-wireless: send an EV_SYN/SYN_REPORT between state changes
platform/x86: dell-wmi-sysman: work around for BIOS bug
platform/x86: mlx-platform: remove an unused variable
platform/x86: thinkpad_acpi: remove trailing semicolon in macro definition
platform/x86: dell-smbios-base: Fix error return code in dell_smbios_init
...

+6599 -261
+258
Documentation/ABI/testing/sysfs-class-firmware-attributes
··· 1 + What: /sys/class/firmware-attributes/*/attributes/*/ 2 + Date: February 2021 3 + KernelVersion: 5.11 4 + Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, 5 + Mario Limonciello <mario.limonciello@dell.com>, 6 + Prasanth KSR <prasanth.ksr@dell.com> 7 + Description: 8 + A sysfs interface for systems management software to enable 9 + configuration capability on supported systems. This directory 10 + exposes interfaces for interacting with configuration options. 11 + 12 + Unless otherwise specified in an attribute description all attributes are optional 13 + and will accept UTF-8 input. 14 + 15 + type: 16 + A file that can be read to obtain the type of attribute. 17 + This attribute is mandatory. 18 + 19 + The following are known types: 20 + 21 + - enumeration: a set of pre-defined valid values 22 + - integer: a range of numerical values 23 + - string 24 + 25 + All attribute types support the following values: 26 + 27 + current_value: 28 + A file that can be read to obtain the current 29 + value of the <attr>. 30 + 31 + This file can also be written to in order to update the value of a 32 + <attr> 33 + 34 + This attribute is mandatory. 35 + 36 + default_value: 37 + A file that can be read to obtain the default 38 + value of the <attr> 39 + 40 + display_name: 41 + A file that can be read to obtain a user friendly 42 + description of the at <attr> 43 + 44 + display_name_language_code: 45 + A file that can be read to obtain 46 + the IETF language tag corresponding to the 47 + "display_name" of the <attr> 48 + 49 + "enumeration"-type specific properties: 50 + 51 + possible_values: 52 + A file that can be read to obtain the possible 53 + values of the <attr>. Values are separated using 54 + semi-colon (``;``). 55 + 56 + "integer"-type specific properties: 57 + 58 + min_value: 59 + A file that can be read to obtain the lower 60 + bound value of the <attr> 61 + 62 + max_value: 63 + A file that can be read to obtain the upper 64 + bound value of the <attr> 65 + 66 + scalar_increment: 67 + A file that can be read to obtain the scalar value used for 68 + increments of current_value this attribute accepts. 69 + 70 + "string"-type specific properties: 71 + 72 + max_length: 73 + A file that can be read to obtain the maximum 74 + length value of the <attr> 75 + 76 + min_length: 77 + A file that can be read to obtain the minimum 78 + length value of the <attr> 79 + 80 + Dell specific class extensions 81 + ------------------------------ 82 + 83 + On Dell systems the following additional attributes are available: 84 + 85 + dell_modifier: 86 + A file that can be read to obtain attribute-level 87 + dependency rule. It says an attribute X will become read-only or 88 + suppressed, if/if-not attribute Y is configured. 89 + 90 + modifier rules can be in following format:: 91 + 92 + [ReadOnlyIf:<attribute>=<value>] 93 + [ReadOnlyIfNot:<attribute>=<value>] 94 + [SuppressIf:<attribute>=<value>] 95 + [SuppressIfNot:<attribute>=<value>] 96 + 97 + For example:: 98 + 99 + AutoOnFri/dell_modifier has value, 100 + [SuppressIfNot:AutoOn=SelectDays] 101 + 102 + This means AutoOnFri will be suppressed in BIOS setup if AutoOn 103 + attribute is not "SelectDays" and its value will not be effective 104 + through sysfs until this rule is met. 105 + 106 + Enumeration attributes also support the following: 107 + 108 + dell_value_modifier: 109 + A file that can be read to obtain value-level dependency. 110 + This file is similar to dell_modifier but here, an 111 + attribute's current value will be forcefully changed based 112 + dependent attributes value. 113 + 114 + dell_value_modifier rules can be in following format:: 115 + 116 + <value>[ForceIf:<attribute>=<value>] 117 + <value>[ForceIfNot:<attribute>=<value>] 118 + 119 + For example: 120 + 121 + LegacyOrom/dell_value_modifier has value: 122 + Disabled[ForceIf:SecureBoot=Enabled] 123 + 124 + This means LegacyOrom's current value will be forced to 125 + "Disabled" in BIOS setup if SecureBoot is Enabled and its 126 + value will not be effective through sysfs until this rule is 127 + met. 128 + 129 + What: /sys/class/firmware-attributes/*/authentication/ 130 + Date: February 2021 131 + KernelVersion: 5.11 132 + Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, 133 + Mario Limonciello <mario.limonciello@dell.com>, 134 + Prasanth KSR <prasanth.ksr@dell.com> 135 + Description: 136 + Devices support various authentication mechanisms which can be exposed 137 + as a separate configuration object. 138 + 139 + For example a "BIOS Admin" password and "System" Password can be set, 140 + reset or cleared using these attributes. 141 + 142 + - An "Admin" password is used for preventing modification to the BIOS 143 + settings. 144 + - A "System" password is required to boot a machine. 145 + 146 + Change in any of these two authentication methods will also generate an 147 + uevent KOBJ_CHANGE. 148 + 149 + is_enabled: 150 + A file that can be read to obtain a 0/1 flag to see if 151 + <attr> authentication is enabled. 152 + This attribute is mandatory. 153 + 154 + role: 155 + The type of authentication used. 156 + This attribute is mandatory. 157 + 158 + Known types: 159 + bios-admin: 160 + Representing BIOS administrator password 161 + power-on: 162 + Representing a password required to use 163 + the system 164 + 165 + mechanism: 166 + The means of authentication. This attribute is mandatory. 167 + Only supported type currently is "password". 168 + 169 + max_password_length: 170 + A file that can be read to obtain the 171 + maximum length of the Password 172 + 173 + min_password_length: 174 + A file that can be read to obtain the 175 + minimum length of the Password 176 + 177 + current_password: 178 + A write only value used for privileged access such as 179 + setting attributes when a system or admin password is set 180 + or resetting to a new password 181 + 182 + This attribute is mandatory when mechanism == "password". 183 + 184 + new_password: 185 + A write only value that when used in tandem with 186 + current_password will reset a system or admin password. 187 + 188 + Note, password management is session specific. If Admin password is set, 189 + same password must be written into current_password file (required for 190 + password-validation) and must be cleared once the session is over. 191 + For example:: 192 + 193 + echo "password" > current_password 194 + echo "disabled" > TouchScreen/current_value 195 + echo "" > current_password 196 + 197 + Drivers may emit a CHANGE uevent when a password is set or unset 198 + userspace may check it again. 199 + 200 + On Dell systems, if Admin password is set, then all BIOS attributes 201 + require password validation. 202 + 203 + What: /sys/class/firmware-attributes/*/attributes/pending_reboot 204 + Date: February 2021 205 + KernelVersion: 5.11 206 + Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, 207 + Mario Limonciello <mario.limonciello@dell.com>, 208 + Prasanth KSR <prasanth.ksr@dell.com> 209 + Description: 210 + A read-only attribute reads 1 if a reboot is necessary to apply 211 + pending BIOS attribute changes. Also, an uevent_KOBJ_CHANGE is 212 + generated when it changes to 1. 213 + 214 + == ========================================= 215 + 0 All BIOS attributes setting are current 216 + 1 A reboot is necessary to get pending BIOS 217 + attribute changes applied 218 + == ========================================= 219 + 220 + Note, userspace applications need to follow below steps for efficient 221 + BIOS management, 222 + 223 + 1. Check if admin password is set. If yes, follow session method for 224 + password management as briefed under authentication section above. 225 + 2. Before setting any attribute, check if it has any modifiers 226 + or value_modifiers. If yes, incorporate them and then modify 227 + attribute. 228 + 229 + Drivers may emit a CHANGE uevent when this value changes and userspace 230 + may check it again. 231 + 232 + What: /sys/class/firmware-attributes/*/attributes/reset_bios 233 + Date: February 2021 234 + KernelVersion: 5.11 235 + Contact: Divya Bharathi <Divya.Bharathi@Dell.com>, 236 + Mario Limonciello <mario.limonciello@dell.com>, 237 + Prasanth KSR <prasanth.ksr@dell.com> 238 + Description: 239 + This attribute can be used to reset the BIOS Configuration. 240 + Specifically, it tells which type of reset BIOS configuration is being 241 + requested on the host. 242 + 243 + Reading from it returns a list of supported options encoded as: 244 + 245 + - 'builtinsafe' (Built in safe configuration profile) 246 + - 'lastknowngood' (Last known good saved configuration profile) 247 + - 'factory' (Default factory settings configuration profile) 248 + - 'custom' (Custom saved configuration profile) 249 + 250 + The currently selected option is printed in square brackets as 251 + shown below:: 252 + 253 + # echo "factory" > /sys/class/firmware-attributes/*/device/attributes/reset_bios 254 + # cat /sys/class/firmware-attributes/*/device/attributes/reset_bios 255 + # builtinsafe lastknowngood [factory] custom 256 + 257 + Note that any changes to this attribute requires a reboot 258 + for changes to take effect.
+119
Documentation/ABI/testing/sysfs-class-intel_pmt
··· 1 + What: /sys/class/intel_pmt/ 2 + Date: October 2020 3 + KernelVersion: 5.10 4 + Contact: David Box <david.e.box@linux.intel.com> 5 + Description: 6 + The intel_pmt/ class directory contains information for 7 + devices that expose hardware telemetry using Intel Platform 8 + Monitoring Technology (PMT) 9 + 10 + What: /sys/class/intel_pmt/telem<x> 11 + Date: October 2020 12 + KernelVersion: 5.10 13 + Contact: David Box <david.e.box@linux.intel.com> 14 + Description: 15 + The telem<x> directory contains files describing an instance of 16 + a PMT telemetry device that exposes hardware telemetry. Each 17 + telem<x> directory has an associated telem file. This file 18 + may be opened and mapped or read to access the telemetry space 19 + of the device. The register layout of the telemetry space is 20 + determined from an XML file that matches the PCI device id and 21 + GUID for the device. 22 + 23 + What: /sys/class/intel_pmt/telem<x>/telem 24 + Date: October 2020 25 + KernelVersion: 5.10 26 + Contact: David Box <david.e.box@linux.intel.com> 27 + Description: 28 + (RO) The telemetry data for this telemetry device. This file 29 + may be mapped or read to obtain the data. 30 + 31 + What: /sys/class/intel_pmt/telem<x>/guid 32 + Date: October 2020 33 + KernelVersion: 5.10 34 + Contact: David Box <david.e.box@linux.intel.com> 35 + Description: 36 + (RO) The GUID for this telemetry device. The GUID identifies 37 + the version of the XML file for the parent device that is to 38 + be used to get the register layout. 39 + 40 + What: /sys/class/intel_pmt/telem<x>/size 41 + Date: October 2020 42 + KernelVersion: 5.10 43 + Contact: David Box <david.e.box@linux.intel.com> 44 + Description: 45 + (RO) The size of telemetry region in bytes that corresponds to 46 + the mapping size for the telem file. 47 + 48 + What: /sys/class/intel_pmt/telem<x>/offset 49 + Date: October 2020 50 + KernelVersion: 5.10 51 + Contact: David Box <david.e.box@linux.intel.com> 52 + Description: 53 + (RO) The offset of telemetry region in bytes that corresponds to 54 + the mapping for the telem file. 55 + 56 + What: /sys/class/intel_pmt/crashlog<x> 57 + Date: October 2020 58 + KernelVersion: 5.10 59 + Contact: Alexander Duyck <alexander.h.duyck@linux.intel.com> 60 + Description: 61 + The crashlog<x> directory contains files for configuring an 62 + instance of a PMT crashlog device that can perform crash data 63 + recording. Each crashlog<x> device has an associated crashlog 64 + file. This file can be opened and mapped or read to access the 65 + resulting crashlog buffer. The register layout for the buffer 66 + can be determined from an XML file of specified GUID for the 67 + parent device. 68 + 69 + What: /sys/class/intel_pmt/crashlog<x>/crashlog 70 + Date: October 2020 71 + KernelVersion: 5.10 72 + Contact: David Box <david.e.box@linux.intel.com> 73 + Description: 74 + (RO) The crashlog buffer for this crashlog device. This file 75 + may be mapped or read to obtain the data. 76 + 77 + What: /sys/class/intel_pmt/crashlog<x>/guid 78 + Date: October 2020 79 + KernelVersion: 5.10 80 + Contact: Alexander Duyck <alexander.h.duyck@linux.intel.com> 81 + Description: 82 + (RO) The GUID for this crashlog device. The GUID identifies the 83 + version of the XML file for the parent device that should be 84 + used to determine the register layout. 85 + 86 + What: /sys/class/intel_pmt/crashlog<x>/size 87 + Date: October 2020 88 + KernelVersion: 5.10 89 + Contact: Alexander Duyck <alexander.h.duyck@linux.intel.com> 90 + Description: 91 + (RO) The length of the result buffer in bytes that corresponds 92 + to the size for the crashlog buffer. 93 + 94 + What: /sys/class/intel_pmt/crashlog<x>/offset 95 + Date: October 2020 96 + KernelVersion: 5.10 97 + Contact: Alexander Duyck <alexander.h.duyck@linux.intel.com> 98 + Description: 99 + (RO) The offset of the buffer in bytes that corresponds 100 + to the mapping for the crashlog device. 101 + 102 + What: /sys/class/intel_pmt/crashlog<x>/enable 103 + Date: October 2020 104 + KernelVersion: 5.10 105 + Contact: Alexander Duyck <alexander.h.duyck@linux.intel.com> 106 + Description: 107 + (RW) Boolean value controlling if the crashlog functionality 108 + is enabled for the crashlog device. 109 + 110 + What: /sys/class/intel_pmt/crashlog<x>/trigger 111 + Date: October 2020 112 + KernelVersion: 5.10 113 + Contact: Alexander Duyck <alexander.h.duyck@linux.intel.com> 114 + Description: 115 + (RW) Boolean value controlling the triggering of the crashlog 116 + device node. When read it provides data on if the crashlog has 117 + been triggered. When written to it can be used to either clear 118 + the current trigger by writing false, or to trigger a new 119 + event if the trigger is not currently set.
+38 -2
MAINTAINERS
··· 929 929 S: Maintained 930 930 F: drivers/i2c/busses/i2c-amd-mp2* 931 931 932 + AMD PMC DRIVER 933 + M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 934 + L: platform-driver-x86@vger.kernel.org 935 + S: Maintained 936 + F: drivers/platform/x86/amd-pmc.* 937 + 932 938 AMD POWERPLAY 933 939 M: Evan Quan <evan.quan@amd.com> 934 940 L: amd-gfx@lists.freedesktop.org ··· 5021 5015 S: Maintained 5022 5016 F: drivers/platform/x86/dell-wmi-descriptor.c 5023 5017 5018 + DELL WMI SYSMAN DRIVER 5019 + M: Divya Bharathi <divya.bharathi@dell.com> 5020 + M: Mario Limonciello <mario.limonciello@dell.com> 5021 + M: Prasanth Ksr <prasanth.ksr@dell.com> 5022 + L: platform-driver-x86@vger.kernel.org 5023 + S: Maintained 5024 + F: Documentation/ABI/testing/sysfs-class-firmware-attributes 5025 + F: drivers/platform/x86/dell-wmi-sysman/ 5026 + 5024 5027 DELL WMI NOTIFICATIONS DRIVER 5025 5028 M: Matthew Garrett <mjg59@srcf.ucam.org> 5026 5029 M: Pali Rohár <pali@kernel.org> ··· 9092 9077 F: include/linux/mfd/intel_msic.h 9093 9078 F: include/linux/mfd/intel_soc_pmic* 9094 9079 9080 + INTEL PMT DRIVER 9081 + M: "David E. Box" <david.e.box@linux.intel.com> 9082 + S: Maintained 9083 + F: drivers/mfd/intel_pmt.c 9084 + F: drivers/platform/x86/intel_pmt_* 9085 + 9095 9086 INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT 9096 9087 M: Stanislav Yakovlev <stas.yakovlev@gmail.com> 9097 9088 L: linux-wireless@vger.kernel.org ··· 11756 11735 F: include/linux/cciss*.h 11757 11736 F: include/uapi/linux/cciss*.h 11758 11737 11738 + MICROSOFT SURFACE GPE LID SUPPORT DRIVER 11739 + M: Maximilian Luz <luzmaximilian@gmail.com> 11740 + L: platform-driver-x86@vger.kernel.org 11741 + S: Maintained 11742 + F: drivers/platform/surface/surface_gpe.c 11743 + 11744 + MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT 11745 + M: Hans de Goede <hdegoede@redhat.com> 11746 + M: Mark Gross <mgross@linux.intel.com> 11747 + M: Maximilian Luz <luzmaximilian@gmail.com> 11748 + L: platform-driver-x86@vger.kernel.org 11749 + S: Maintained 11750 + T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git 11751 + F: drivers/platform/surface/ 11752 + 11759 11753 MICROSOFT SURFACE PRO 3 BUTTON DRIVER 11760 11754 M: Chen Yu <yu.c.chen@intel.com> 11761 11755 L: platform-driver-x86@vger.kernel.org 11762 11756 S: Supported 11763 - F: drivers/platform/x86/surfacepro3_button.c 11757 + F: drivers/platform/surface/surfacepro3_button.c 11764 11758 11765 11759 MICROTEK X6 SCANNER 11766 11760 M: Oliver Neukum <oliver@neukum.org> ··· 13356 13320 F: net/core/page_pool.c 13357 13321 13358 13322 PANASONIC LAPTOP ACPI EXTRAS DRIVER 13359 - M: Harald Welte <laforge@gnumonks.org> 13323 + M: Kenneth Chan <kenneth.t.chan@gmail.com> 13360 13324 L: platform-driver-x86@vger.kernel.org 13361 13325 S: Maintained 13362 13326 F: drivers/platform/x86/panasonic-laptop.c
+10
drivers/mfd/Kconfig
··· 682 682 Register and P-unit access. In addition this creates devices 683 683 for iTCO watchdog and telemetry that are part of the PMC. 684 684 685 + config MFD_INTEL_PMT 686 + tristate "Intel Platform Monitoring Technology (PMT) support" 687 + depends on PCI 688 + select MFD_CORE 689 + help 690 + The Intel Platform Monitoring Technology (PMT) is an interface that 691 + provides access to hardware monitor registers. This driver supports 692 + Telemetry, Watcher, and Crashlog PMT capabilities/devices for 693 + platforms starting from Tiger Lake. 694 + 685 695 config MFD_IPAQ_MICRO 686 696 bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" 687 697 depends on SA1100_H3100 || SA1100_H3600
+1
drivers/mfd/Makefile
··· 216 216 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o 217 217 obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o 218 218 obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o 219 + obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o 219 220 obj-$(CONFIG_MFD_PALMAS) += palmas.o 220 221 obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o 221 222 obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
+223
drivers/mfd/intel_pmt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitoring Technology PMT driver 4 + * 5 + * Copyright (c) 2020, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + * Author: David E. Box <david.e.box@linux.intel.com> 9 + */ 10 + 11 + #include <linux/bits.h> 12 + #include <linux/kernel.h> 13 + #include <linux/mfd/core.h> 14 + #include <linux/module.h> 15 + #include <linux/pci.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/pm.h> 18 + #include <linux/pm_runtime.h> 19 + #include <linux/types.h> 20 + 21 + /* Intel DVSEC capability vendor space offsets */ 22 + #define INTEL_DVSEC_ENTRIES 0xA 23 + #define INTEL_DVSEC_SIZE 0xB 24 + #define INTEL_DVSEC_TABLE 0xC 25 + #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) 26 + #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) 27 + #define INTEL_DVSEC_ENTRY_SIZE 4 28 + 29 + /* PMT capabilities */ 30 + #define DVSEC_INTEL_ID_TELEMETRY 2 31 + #define DVSEC_INTEL_ID_WATCHER 3 32 + #define DVSEC_INTEL_ID_CRASHLOG 4 33 + 34 + struct intel_dvsec_header { 35 + u16 length; 36 + u16 id; 37 + u8 num_entries; 38 + u8 entry_size; 39 + u8 tbir; 40 + u32 offset; 41 + }; 42 + 43 + enum pmt_quirks { 44 + /* Watcher capability not supported */ 45 + PMT_QUIRK_NO_WATCHER = BIT(0), 46 + 47 + /* Crashlog capability not supported */ 48 + PMT_QUIRK_NO_CRASHLOG = BIT(1), 49 + 50 + /* Use shift instead of mask to read discovery table offset */ 51 + PMT_QUIRK_TABLE_SHIFT = BIT(2), 52 + }; 53 + 54 + struct pmt_platform_info { 55 + unsigned long quirks; 56 + }; 57 + 58 + static const struct pmt_platform_info tgl_info = { 59 + .quirks = PMT_QUIRK_NO_WATCHER | PMT_QUIRK_NO_CRASHLOG | 60 + PMT_QUIRK_TABLE_SHIFT, 61 + }; 62 + 63 + static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header, 64 + unsigned long quirks) 65 + { 66 + struct device *dev = &pdev->dev; 67 + struct resource *res, *tmp; 68 + struct mfd_cell *cell; 69 + const char *name; 70 + int count = header->num_entries; 71 + int size = header->entry_size; 72 + int id = header->id; 73 + int i; 74 + 75 + switch (id) { 76 + case DVSEC_INTEL_ID_TELEMETRY: 77 + name = "pmt_telemetry"; 78 + break; 79 + case DVSEC_INTEL_ID_WATCHER: 80 + if (quirks & PMT_QUIRK_NO_WATCHER) { 81 + dev_info(dev, "Watcher not supported\n"); 82 + return 0; 83 + } 84 + name = "pmt_watcher"; 85 + break; 86 + case DVSEC_INTEL_ID_CRASHLOG: 87 + if (quirks & PMT_QUIRK_NO_CRASHLOG) { 88 + dev_info(dev, "Crashlog not supported\n"); 89 + return 0; 90 + } 91 + name = "pmt_crashlog"; 92 + break; 93 + default: 94 + dev_err(dev, "Unrecognized PMT capability: %d\n", id); 95 + return -EINVAL; 96 + } 97 + 98 + if (!header->num_entries || !header->entry_size) { 99 + dev_err(dev, "Invalid count or size for %s header\n", name); 100 + return -EINVAL; 101 + } 102 + 103 + cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL); 104 + if (!cell) 105 + return -ENOMEM; 106 + 107 + res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL); 108 + if (!res) 109 + return -ENOMEM; 110 + 111 + if (quirks & PMT_QUIRK_TABLE_SHIFT) 112 + header->offset >>= 3; 113 + 114 + /* 115 + * The PMT DVSEC contains the starting offset and count for a block of 116 + * discovery tables, each providing access to monitoring facilities for 117 + * a section of the device. Create a resource list of these tables to 118 + * provide to the driver. 119 + */ 120 + for (i = 0, tmp = res; i < count; i++, tmp++) { 121 + tmp->start = pdev->resource[header->tbir].start + 122 + header->offset + i * (size << 2); 123 + tmp->end = tmp->start + (size << 2) - 1; 124 + tmp->flags = IORESOURCE_MEM; 125 + } 126 + 127 + cell->resources = res; 128 + cell->num_resources = count; 129 + cell->name = name; 130 + 131 + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, 132 + NULL); 133 + } 134 + 135 + static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 136 + { 137 + struct pmt_platform_info *info; 138 + unsigned long quirks = 0; 139 + bool found_devices = false; 140 + int ret, pos = 0; 141 + 142 + ret = pcim_enable_device(pdev); 143 + if (ret) 144 + return ret; 145 + 146 + info = (struct pmt_platform_info *)id->driver_data; 147 + 148 + if (info) 149 + quirks = info->quirks; 150 + 151 + do { 152 + struct intel_dvsec_header header; 153 + u32 table; 154 + u16 vid; 155 + 156 + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); 157 + if (!pos) 158 + break; 159 + 160 + pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid); 161 + if (vid != PCI_VENDOR_ID_INTEL) 162 + continue; 163 + 164 + pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, 165 + &header.id); 166 + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, 167 + &header.num_entries); 168 + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, 169 + &header.entry_size); 170 + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, 171 + &table); 172 + 173 + header.tbir = INTEL_DVSEC_TABLE_BAR(table); 174 + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 175 + 176 + ret = pmt_add_dev(pdev, &header, quirks); 177 + if (ret) { 178 + dev_warn(&pdev->dev, 179 + "Failed to add device for DVSEC id %d\n", 180 + header.id); 181 + continue; 182 + } 183 + 184 + found_devices = true; 185 + } while (true); 186 + 187 + if (!found_devices) 188 + return -ENODEV; 189 + 190 + pm_runtime_put(&pdev->dev); 191 + pm_runtime_allow(&pdev->dev); 192 + 193 + return 0; 194 + } 195 + 196 + static void pmt_pci_remove(struct pci_dev *pdev) 197 + { 198 + pm_runtime_forbid(&pdev->dev); 199 + pm_runtime_get_sync(&pdev->dev); 200 + } 201 + 202 + #define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d 203 + #define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7 204 + #define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d 205 + static const struct pci_device_id pmt_pci_ids[] = { 206 + { PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) }, 207 + { PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) }, 208 + { PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) }, 209 + { } 210 + }; 211 + MODULE_DEVICE_TABLE(pci, pmt_pci_ids); 212 + 213 + static struct pci_driver pmt_pci_driver = { 214 + .name = "intel-pmt", 215 + .id_table = pmt_pci_ids, 216 + .probe = pmt_pci_probe, 217 + .remove = pmt_pci_remove, 218 + }; 219 + module_pci_driver(pmt_pci_driver); 220 + 221 + MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 222 + MODULE_DESCRIPTION("Intel Platform Monitoring Technology PMT driver"); 223 + MODULE_LICENSE("GPL v2");
+2
drivers/platform/Kconfig
··· 13 13 source "drivers/platform/mellanox/Kconfig" 14 14 15 15 source "drivers/platform/olpc/Kconfig" 16 + 17 + source "drivers/platform/surface/Kconfig"
+1
drivers/platform/Makefile
··· 9 9 obj-$(CONFIG_OLPC_EC) += olpc/ 10 10 obj-$(CONFIG_GOLDFISH) += goldfish/ 11 11 obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ 12 + obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+10
drivers/platform/mellanox/Kconfig
··· 56 56 to the userspace tools, to be used in conjunction with the eMMC 57 57 device driver to do necessary initial swap of the boot partition. 58 58 59 + config MLXBF_PMC 60 + tristate "Mellanox BlueField Performance Monitoring Counters driver" 61 + depends on ARM64 62 + depends on HWMON 63 + depends on ACPI 64 + help 65 + Say y here to enable PMC support. The PMC driver provides access 66 + to performance monitoring counters within various blocks in the 67 + Mellanox BlueField SoC via a sysfs interface. 68 + 59 69 endif # MELLANOX_PLATFORM
+1
drivers/platform/mellanox/Makefile
··· 4 4 # Mellanox Platform-Specific Drivers 5 5 # 6 6 obj-$(CONFIG_MLXBF_BOOTCTL) += mlxbf-bootctl.o 7 + obj-$(CONFIG_MLXBF_PMC) += mlxbf-pmc.o 7 8 obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o 8 9 obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o 9 10 obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
+1478
drivers/platform/mellanox/mlxbf-pmc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR Linux-OpenIB 2 + /* 3 + * Mellanox BlueField Performance Monitoring Counters driver 4 + * 5 + * This driver provides a sysfs interface for monitoring 6 + * performance statistics in BlueField SoC. 7 + * 8 + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. 9 + */ 10 + 11 + #include <linux/acpi.h> 12 + #include <linux/arm-smccc.h> 13 + #include <linux/bitfield.h> 14 + #include <linux/errno.h> 15 + #include <linux/hwmon.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/string.h> 18 + #include <uapi/linux/psci.h> 19 + 20 + #define MLXBF_PMC_WRITE_REG_32 0x82000009 21 + #define MLXBF_PMC_READ_REG_32 0x8200000A 22 + #define MLXBF_PMC_WRITE_REG_64 0x8200000B 23 + #define MLXBF_PMC_READ_REG_64 0x8200000C 24 + #define MLXBF_PMC_SIP_SVC_UID 0x8200ff01 25 + #define MLXBF_PMC_SIP_SVC_VERSION 0x8200ff03 26 + #define MLXBF_PMC_SVC_REQ_MAJOR 0 27 + #define MLXBF_PMC_SVC_MIN_MINOR 3 28 + 29 + #define MLXBF_PMC_SMCCC_ACCESS_VIOLATION -4 30 + 31 + #define MLXBF_PMC_EVENT_SET_BF1 0 32 + #define MLXBF_PMC_EVENT_SET_BF2 1 33 + #define MLXBF_PMC_EVENT_INFO_LEN 100 34 + 35 + #define MLXBF_PMC_MAX_BLOCKS 30 36 + #define MLXBF_PMC_MAX_ATTRS 30 37 + #define MLXBF_PMC_INFO_SZ 4 38 + #define MLXBF_PMC_REG_SIZE 8 39 + #define MLXBF_PMC_L3C_REG_SIZE 4 40 + 41 + #define MLXBF_PMC_TYPE_COUNTER 1 42 + #define MLXBF_PMC_TYPE_REGISTER 0 43 + 44 + #define MLXBF_PMC_PERFCTL 0 45 + #define MLXBF_PMC_PERFEVT 1 46 + #define MLXBF_PMC_PERFACC0 4 47 + 48 + #define MLXBF_PMC_PERFMON_CONFIG_WR_R_B BIT(0) 49 + #define MLXBF_PMC_PERFMON_CONFIG_STROBE BIT(1) 50 + #define MLXBF_PMC_PERFMON_CONFIG_ADDR GENMASK_ULL(4, 2) 51 + #define MLXBF_PMC_PERFMON_CONFIG_WDATA GENMASK_ULL(60, 5) 52 + 53 + #define MLXBF_PMC_PERFCTL_FM0 GENMASK_ULL(18, 16) 54 + #define MLXBF_PMC_PERFCTL_MS0 GENMASK_ULL(21, 20) 55 + #define MLXBF_PMC_PERFCTL_ACCM0 GENMASK_ULL(26, 24) 56 + #define MLXBF_PMC_PERFCTL_AD0 BIT(27) 57 + #define MLXBF_PMC_PERFCTL_ETRIG0 GENMASK_ULL(29, 28) 58 + #define MLXBF_PMC_PERFCTL_EB0 BIT(30) 59 + #define MLXBF_PMC_PERFCTL_EN0 BIT(31) 60 + 61 + #define MLXBF_PMC_PERFEVT_EVTSEL GENMASK_ULL(31, 24) 62 + 63 + #define MLXBF_PMC_L3C_PERF_CNT_CFG 0x0 64 + #define MLXBF_PMC_L3C_PERF_CNT_SEL 0x10 65 + #define MLXBF_PMC_L3C_PERF_CNT_SEL_1 0x14 66 + #define MLXBF_PMC_L3C_PERF_CNT_LOW 0x40 67 + #define MLXBF_PMC_L3C_PERF_CNT_HIGH 0x60 68 + 69 + #define MLXBF_PMC_L3C_PERF_CNT_CFG_EN BIT(0) 70 + #define MLXBF_PMC_L3C_PERF_CNT_CFG_RST BIT(1) 71 + #define MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_0 GENMASK(5, 0) 72 + #define MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_1 GENMASK(13, 8) 73 + #define MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_2 GENMASK(21, 16) 74 + #define MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_3 GENMASK(29, 24) 75 + 76 + #define MLXBF_PMC_L3C_PERF_CNT_SEL_1_CNT_4 GENMASK(5, 0) 77 + 78 + #define MLXBF_PMC_L3C_PERF_CNT_LOW_VAL GENMASK(31, 0) 79 + #define MLXBF_PMC_L3C_PERF_CNT_HIGH_VAL GENMASK(24, 0) 80 + 81 + /** 82 + * Structure to hold attribute and block info for each sysfs entry 83 + * @dev_attr: Device attribute struct 84 + * @index: index to identify counter number within a block 85 + * @nr: block number to which the sysfs belongs 86 + */ 87 + struct mlxbf_pmc_attribute { 88 + struct device_attribute dev_attr; 89 + int index; 90 + int nr; 91 + }; 92 + 93 + /** 94 + * Structure to hold info for each HW block 95 + * 96 + * @mmio_base: The VA at which the PMC block is mapped 97 + * @blk_size: Size of each mapped region 98 + * @counters: Number of counters in the block 99 + * @type: Type of counters in the block 100 + * @attr_counter: Attributes for "counter" sysfs files 101 + * @attr_event: Attributes for "event" sysfs files 102 + * @attr_event_list: Attributes for "event_list" sysfs files 103 + * @attr_enable: Attributes for "enable" sysfs files 104 + * @block_attr: All attributes needed for the block 105 + * @blcok_attr_grp: Attribute group for the block 106 + */ 107 + struct mlxbf_pmc_block_info { 108 + void __iomem *mmio_base; 109 + size_t blk_size; 110 + size_t counters; 111 + int type; 112 + struct mlxbf_pmc_attribute *attr_counter; 113 + struct mlxbf_pmc_attribute *attr_event; 114 + struct mlxbf_pmc_attribute attr_event_list; 115 + struct mlxbf_pmc_attribute attr_enable; 116 + struct attribute *block_attr[MLXBF_PMC_MAX_ATTRS]; 117 + struct attribute_group block_attr_grp; 118 + }; 119 + 120 + /** 121 + * Structure to hold PMC context info 122 + * 123 + * @pdev: The kernel structure representing the device 124 + * @total_blocks: Total number of blocks 125 + * @tile_count: Number of tiles in the system 126 + * @hwmon_dev: Hwmon device for bfperf 127 + * @block_name: Block name 128 + * @block: Block info 129 + * @groups: Attribute groups from each block 130 + * @sv_sreg_support: Whether SMCs are used to access performance registers 131 + * @sreg_tbl_perf: Secure register access table number 132 + * @event_set: Event set to use 133 + */ 134 + struct mlxbf_pmc_context { 135 + struct platform_device *pdev; 136 + uint32_t total_blocks; 137 + uint32_t tile_count; 138 + struct device *hwmon_dev; 139 + const char *block_name[MLXBF_PMC_MAX_BLOCKS]; 140 + struct mlxbf_pmc_block_info block[MLXBF_PMC_MAX_BLOCKS]; 141 + const struct attribute_group *groups[MLXBF_PMC_MAX_BLOCKS]; 142 + bool svc_sreg_support; 143 + uint32_t sreg_tbl_perf; 144 + unsigned int event_set; 145 + }; 146 + 147 + /** 148 + * Structure to hold supported events for each block 149 + * @evt_num: Event number used to program counters 150 + * @evt_name: Name of the event 151 + */ 152 + struct mlxbf_pmc_events { 153 + int evt_num; 154 + char *evt_name; 155 + }; 156 + 157 + static const struct mlxbf_pmc_events mlxbf_pmc_pcie_events[] = { 158 + { 0x0, "IN_P_PKT_CNT" }, 159 + { 0x10, "IN_NP_PKT_CNT" }, 160 + { 0x18, "IN_C_PKT_CNT" }, 161 + { 0x20, "OUT_P_PKT_CNT" }, 162 + { 0x28, "OUT_NP_PKT_CNT" }, 163 + { 0x30, "OUT_C_PKT_CNT" }, 164 + { 0x38, "IN_P_BYTE_CNT" }, 165 + { 0x40, "IN_NP_BYTE_CNT" }, 166 + { 0x48, "IN_C_BYTE_CNT" }, 167 + { 0x50, "OUT_P_BYTE_CNT" }, 168 + { 0x58, "OUT_NP_BYTE_CNT" }, 169 + { 0x60, "OUT_C_BYTE_CNT" }, 170 + }; 171 + 172 + static const struct mlxbf_pmc_events mlxbf_pmc_smgen_events[] = { 173 + { 0x0, "AW_REQ" }, 174 + { 0x1, "AW_BEATS" }, 175 + { 0x2, "AW_TRANS" }, 176 + { 0x3, "AW_RESP" }, 177 + { 0x4, "AW_STL" }, 178 + { 0x5, "AW_LAT" }, 179 + { 0x6, "AW_REQ_TBU" }, 180 + { 0x8, "AR_REQ" }, 181 + { 0x9, "AR_BEATS" }, 182 + { 0xa, "AR_TRANS" }, 183 + { 0xb, "AR_STL" }, 184 + { 0xc, "AR_LAT" }, 185 + { 0xd, "AR_REQ_TBU" }, 186 + { 0xe, "TBU_MISS" }, 187 + { 0xf, "TX_DAT_AF" }, 188 + { 0x10, "RX_DAT_AF" }, 189 + { 0x11, "RETRYQ_CRED" }, 190 + }; 191 + 192 + static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_1[] = { 193 + { 0xa0, "TPIO_DATA_BEAT" }, 194 + { 0xa1, "TDMA_DATA_BEAT" }, 195 + { 0xa2, "MAP_DATA_BEAT" }, 196 + { 0xa3, "TXMSG_DATA_BEAT" }, 197 + { 0xa4, "TPIO_DATA_PACKET" }, 198 + { 0xa5, "TDMA_DATA_PACKET" }, 199 + { 0xa6, "MAP_DATA_PACKET" }, 200 + { 0xa7, "TXMSG_DATA_PACKET" }, 201 + { 0xa8, "TDMA_RT_AF" }, 202 + { 0xa9, "TDMA_PBUF_MAC_AF" }, 203 + { 0xaa, "TRIO_MAP_WRQ_BUF_EMPTY" }, 204 + { 0xab, "TRIO_MAP_CPL_BUF_EMPTY" }, 205 + { 0xac, "TRIO_MAP_RDQ0_BUF_EMPTY" }, 206 + { 0xad, "TRIO_MAP_RDQ1_BUF_EMPTY" }, 207 + { 0xae, "TRIO_MAP_RDQ2_BUF_EMPTY" }, 208 + { 0xaf, "TRIO_MAP_RDQ3_BUF_EMPTY" }, 209 + { 0xb0, "TRIO_MAP_RDQ4_BUF_EMPTY" }, 210 + { 0xb1, "TRIO_MAP_RDQ5_BUF_EMPTY" }, 211 + { 0xb2, "TRIO_MAP_RDQ6_BUF_EMPTY" }, 212 + { 0xb3, "TRIO_MAP_RDQ7_BUF_EMPTY" }, 213 + }; 214 + 215 + static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_2[] = { 216 + { 0xa0, "TPIO_DATA_BEAT" }, 217 + { 0xa1, "TDMA_DATA_BEAT" }, 218 + { 0xa2, "MAP_DATA_BEAT" }, 219 + { 0xa3, "TXMSG_DATA_BEAT" }, 220 + { 0xa4, "TPIO_DATA_PACKET" }, 221 + { 0xa5, "TDMA_DATA_PACKET" }, 222 + { 0xa6, "MAP_DATA_PACKET" }, 223 + { 0xa7, "TXMSG_DATA_PACKET" }, 224 + { 0xa8, "TDMA_RT_AF" }, 225 + { 0xa9, "TDMA_PBUF_MAC_AF" }, 226 + { 0xaa, "TRIO_MAP_WRQ_BUF_EMPTY" }, 227 + { 0xab, "TRIO_MAP_CPL_BUF_EMPTY" }, 228 + { 0xac, "TRIO_MAP_RDQ0_BUF_EMPTY" }, 229 + { 0xad, "TRIO_MAP_RDQ1_BUF_EMPTY" }, 230 + { 0xae, "TRIO_MAP_RDQ2_BUF_EMPTY" }, 231 + { 0xaf, "TRIO_MAP_RDQ3_BUF_EMPTY" }, 232 + { 0xb0, "TRIO_MAP_RDQ4_BUF_EMPTY" }, 233 + { 0xb1, "TRIO_MAP_RDQ5_BUF_EMPTY" }, 234 + { 0xb2, "TRIO_MAP_RDQ6_BUF_EMPTY" }, 235 + { 0xb3, "TRIO_MAP_RDQ7_BUF_EMPTY" }, 236 + { 0xb4, "TRIO_RING_TX_FLIT_CH0" }, 237 + { 0xb5, "TRIO_RING_TX_FLIT_CH1" }, 238 + { 0xb6, "TRIO_RING_TX_FLIT_CH2" }, 239 + { 0xb7, "TRIO_RING_TX_FLIT_CH3" }, 240 + { 0xb8, "TRIO_RING_TX_FLIT_CH4" }, 241 + { 0xb9, "TRIO_RING_RX_FLIT_CH0" }, 242 + { 0xba, "TRIO_RING_RX_FLIT_CH1" }, 243 + { 0xbb, "TRIO_RING_RX_FLIT_CH2" }, 244 + { 0xbc, "TRIO_RING_RX_FLIT_CH3" }, 245 + }; 246 + 247 + static const struct mlxbf_pmc_events mlxbf_pmc_ecc_events[] = { 248 + { 0x100, "ECC_SINGLE_ERROR_CNT" }, 249 + { 0x104, "ECC_DOUBLE_ERROR_CNT" }, 250 + { 0x114, "SERR_INJ" }, 251 + { 0x118, "DERR_INJ" }, 252 + { 0x124, "ECC_SINGLE_ERROR_0" }, 253 + { 0x164, "ECC_DOUBLE_ERROR_0" }, 254 + { 0x340, "DRAM_ECC_COUNT" }, 255 + { 0x344, "DRAM_ECC_INJECT" }, 256 + { 0x348, "DRAM_ECC_ERROR" }, 257 + }; 258 + 259 + static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { 260 + { 0xc0, "RXREQ_MSS" }, 261 + { 0xc1, "RXDAT_MSS" }, 262 + { 0xc2, "TXRSP_MSS" }, 263 + { 0xc3, "TXDAT_MSS" }, 264 + }; 265 + 266 + static const struct mlxbf_pmc_events mlxbf_pmc_hnf_events[] = { 267 + { 0x45, "HNF_REQUESTS" }, 268 + { 0x46, "HNF_REJECTS" }, 269 + { 0x47, "ALL_BUSY" }, 270 + { 0x48, "MAF_BUSY" }, 271 + { 0x49, "MAF_REQUESTS" }, 272 + { 0x4a, "RNF_REQUESTS" }, 273 + { 0x4b, "REQUEST_TYPE" }, 274 + { 0x4c, "MEMORY_READS" }, 275 + { 0x4d, "MEMORY_WRITES" }, 276 + { 0x4e, "VICTIM_WRITE" }, 277 + { 0x4f, "POC_FULL" }, 278 + { 0x50, "POC_FAIL" }, 279 + { 0x51, "POC_SUCCESS" }, 280 + { 0x52, "POC_WRITES" }, 281 + { 0x53, "POC_READS" }, 282 + { 0x54, "FORWARD" }, 283 + { 0x55, "RXREQ_HNF" }, 284 + { 0x56, "RXRSP_HNF" }, 285 + { 0x57, "RXDAT_HNF" }, 286 + { 0x58, "TXREQ_HNF" }, 287 + { 0x59, "TXRSP_HNF" }, 288 + { 0x5a, "TXDAT_HNF" }, 289 + { 0x5b, "TXSNP_HNF" }, 290 + { 0x5c, "INDEX_MATCH" }, 291 + { 0x5d, "A72_ACCESS" }, 292 + { 0x5e, "IO_ACCESS" }, 293 + { 0x5f, "TSO_WRITE" }, 294 + { 0x60, "TSO_CONFLICT" }, 295 + { 0x61, "DIR_HIT" }, 296 + { 0x62, "HNF_ACCEPTS" }, 297 + { 0x63, "REQ_BUF_EMPTY" }, 298 + { 0x64, "REQ_BUF_IDLE_MAF" }, 299 + { 0x65, "TSO_NOARB" }, 300 + { 0x66, "TSO_NOARB_CYCLES" }, 301 + { 0x67, "MSS_NO_CREDIT" }, 302 + { 0x68, "TXDAT_NO_LCRD" }, 303 + { 0x69, "TXSNP_NO_LCRD" }, 304 + { 0x6a, "TXRSP_NO_LCRD" }, 305 + { 0x6b, "TXREQ_NO_LCRD" }, 306 + { 0x6c, "TSO_CL_MATCH" }, 307 + { 0x6d, "MEMORY_READS_BYPASS" }, 308 + { 0x6e, "TSO_NOARB_TIMEOUT" }, 309 + { 0x6f, "ALLOCATE" }, 310 + { 0x70, "VICTIM" }, 311 + { 0x71, "A72_WRITE" }, 312 + { 0x72, "A72_READ" }, 313 + { 0x73, "IO_WRITE" }, 314 + { 0x74, "IO_READ" }, 315 + { 0x75, "TSO_REJECT" }, 316 + { 0x80, "TXREQ_RN" }, 317 + { 0x81, "TXRSP_RN" }, 318 + { 0x82, "TXDAT_RN" }, 319 + { 0x83, "RXSNP_RN" }, 320 + { 0x84, "RXRSP_RN" }, 321 + { 0x85, "RXDAT_RN" }, 322 + }; 323 + 324 + static const struct mlxbf_pmc_events mlxbf_pmc_hnfnet_events[] = { 325 + { 0x12, "CDN_REQ" }, 326 + { 0x13, "DDN_REQ" }, 327 + { 0x14, "NDN_REQ" }, 328 + { 0x15, "CDN_DIAG_N_OUT_OF_CRED" }, 329 + { 0x16, "CDN_DIAG_S_OUT_OF_CRED" }, 330 + { 0x17, "CDN_DIAG_E_OUT_OF_CRED" }, 331 + { 0x18, "CDN_DIAG_W_OUT_OF_CRED" }, 332 + { 0x19, "CDN_DIAG_C_OUT_OF_CRED" }, 333 + { 0x1a, "CDN_DIAG_N_EGRESS" }, 334 + { 0x1b, "CDN_DIAG_S_EGRESS" }, 335 + { 0x1c, "CDN_DIAG_E_EGRESS" }, 336 + { 0x1d, "CDN_DIAG_W_EGRESS" }, 337 + { 0x1e, "CDN_DIAG_C_EGRESS" }, 338 + { 0x1f, "CDN_DIAG_N_INGRESS" }, 339 + { 0x20, "CDN_DIAG_S_INGRESS" }, 340 + { 0x21, "CDN_DIAG_E_INGRESS" }, 341 + { 0x22, "CDN_DIAG_W_INGRESS" }, 342 + { 0x23, "CDN_DIAG_C_INGRESS" }, 343 + { 0x24, "CDN_DIAG_CORE_SENT" }, 344 + { 0x25, "DDN_DIAG_N_OUT_OF_CRED" }, 345 + { 0x26, "DDN_DIAG_S_OUT_OF_CRED" }, 346 + { 0x27, "DDN_DIAG_E_OUT_OF_CRED" }, 347 + { 0x28, "DDN_DIAG_W_OUT_OF_CRED" }, 348 + { 0x29, "DDN_DIAG_C_OUT_OF_CRED" }, 349 + { 0x2a, "DDN_DIAG_N_EGRESS" }, 350 + { 0x2b, "DDN_DIAG_S_EGRESS" }, 351 + { 0x2c, "DDN_DIAG_E_EGRESS" }, 352 + { 0x2d, "DDN_DIAG_W_EGRESS" }, 353 + { 0x2e, "DDN_DIAG_C_EGRESS" }, 354 + { 0x2f, "DDN_DIAG_N_INGRESS" }, 355 + { 0x30, "DDN_DIAG_S_INGRESS" }, 356 + { 0x31, "DDN_DIAG_E_INGRESS" }, 357 + { 0x32, "DDN_DIAG_W_INGRESS" }, 358 + { 0x33, "DDN_DIAG_C_INGRESS" }, 359 + { 0x34, "DDN_DIAG_CORE_SENT" }, 360 + { 0x35, "NDN_DIAG_S_OUT_OF_CRED" }, 361 + { 0x36, "NDN_DIAG_S_OUT_OF_CRED" }, 362 + { 0x37, "NDN_DIAG_E_OUT_OF_CRED" }, 363 + { 0x38, "NDN_DIAG_W_OUT_OF_CRED" }, 364 + { 0x39, "NDN_DIAG_C_OUT_OF_CRED" }, 365 + { 0x3a, "NDN_DIAG_N_EGRESS" }, 366 + { 0x3b, "NDN_DIAG_S_EGRESS" }, 367 + { 0x3c, "NDN_DIAG_E_EGRESS" }, 368 + { 0x3d, "NDN_DIAG_W_EGRESS" }, 369 + { 0x3e, "NDN_DIAG_C_EGRESS" }, 370 + { 0x3f, "NDN_DIAG_N_INGRESS" }, 371 + { 0x40, "NDN_DIAG_S_INGRESS" }, 372 + { 0x41, "NDN_DIAG_E_INGRESS" }, 373 + { 0x42, "NDN_DIAG_W_INGRESS" }, 374 + { 0x43, "NDN_DIAG_C_INGRESS" }, 375 + { 0x44, "NDN_DIAG_CORE_SENT" }, 376 + }; 377 + 378 + static const struct mlxbf_pmc_events mlxbf_pmc_l3c_events[] = { 379 + { 0x00, "DISABLE" }, 380 + { 0x01, "CYCLES" }, 381 + { 0x02, "TOTAL_RD_REQ_IN" }, 382 + { 0x03, "TOTAL_WR_REQ_IN" }, 383 + { 0x04, "TOTAL_WR_DBID_ACK" }, 384 + { 0x05, "TOTAL_WR_DATA_IN" }, 385 + { 0x06, "TOTAL_WR_COMP" }, 386 + { 0x07, "TOTAL_RD_DATA_OUT" }, 387 + { 0x08, "TOTAL_CDN_REQ_IN_BANK0" }, 388 + { 0x09, "TOTAL_CDN_REQ_IN_BANK1" }, 389 + { 0x0a, "TOTAL_DDN_REQ_IN_BANK0" }, 390 + { 0x0b, "TOTAL_DDN_REQ_IN_BANK1" }, 391 + { 0x0c, "TOTAL_EMEM_RD_RES_IN_BANK0" }, 392 + { 0x0d, "TOTAL_EMEM_RD_RES_IN_BANK1" }, 393 + { 0x0e, "TOTAL_CACHE_RD_RES_IN_BANK0" }, 394 + { 0x0f, "TOTAL_CACHE_RD_RES_IN_BANK1" }, 395 + { 0x10, "TOTAL_EMEM_RD_REQ_BANK0" }, 396 + { 0x11, "TOTAL_EMEM_RD_REQ_BANK1" }, 397 + { 0x12, "TOTAL_EMEM_WR_REQ_BANK0" }, 398 + { 0x13, "TOTAL_EMEM_WR_REQ_BANK1" }, 399 + { 0x14, "TOTAL_RD_REQ_OUT" }, 400 + { 0x15, "TOTAL_WR_REQ_OUT" }, 401 + { 0x16, "TOTAL_RD_RES_IN" }, 402 + { 0x17, "HITS_BANK0" }, 403 + { 0x18, "HITS_BANK1" }, 404 + { 0x19, "MISSES_BANK0" }, 405 + { 0x1a, "MISSES_BANK1" }, 406 + { 0x1b, "ALLOCATIONS_BANK0" }, 407 + { 0x1c, "ALLOCATIONS_BANK1" }, 408 + { 0x1d, "EVICTIONS_BANK0" }, 409 + { 0x1e, "EVICTIONS_BANK1" }, 410 + { 0x1f, "DBID_REJECT" }, 411 + { 0x20, "WRDB_REJECT_BANK0" }, 412 + { 0x21, "WRDB_REJECT_BANK1" }, 413 + { 0x22, "CMDQ_REJECT_BANK0" }, 414 + { 0x23, "CMDQ_REJECT_BANK1" }, 415 + { 0x24, "COB_REJECT_BANK0" }, 416 + { 0x25, "COB_REJECT_BANK1" }, 417 + { 0x26, "TRB_REJECT_BANK0" }, 418 + { 0x27, "TRB_REJECT_BANK1" }, 419 + { 0x28, "TAG_REJECT_BANK0" }, 420 + { 0x29, "TAG_REJECT_BANK1" }, 421 + { 0x2a, "ANY_REJECT_BANK0" }, 422 + { 0x2b, "ANY_REJECT_BANK1" }, 423 + }; 424 + 425 + static struct mlxbf_pmc_context *pmc; 426 + 427 + /* UUID used to probe ATF service. */ 428 + static const char *mlxbf_pmc_svc_uuid_str = "89c036b4-e7d7-11e6-8797-001aca00bfc4"; 429 + 430 + /* Calls an SMC to access a performance register */ 431 + static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command, 432 + uint64_t *result) 433 + { 434 + struct arm_smccc_res res; 435 + int status, err = 0; 436 + 437 + arm_smccc_smc(command, pmc->sreg_tbl_perf, (uintptr_t)addr, 0, 0, 0, 0, 438 + 0, &res); 439 + 440 + status = res.a0; 441 + 442 + switch (status) { 443 + case PSCI_RET_NOT_SUPPORTED: 444 + err = -EINVAL; 445 + break; 446 + case MLXBF_PMC_SMCCC_ACCESS_VIOLATION: 447 + err = -EACCES; 448 + break; 449 + default: 450 + *result = res.a1; 451 + break; 452 + } 453 + 454 + return err; 455 + } 456 + 457 + /* Read from a performance counter */ 458 + static int mlxbf_pmc_read(void __iomem *addr, uint32_t command, 459 + uint64_t *result) 460 + { 461 + if (pmc->svc_sreg_support) 462 + return mlxbf_pmc_secure_read(addr, command, result); 463 + 464 + if (command == MLXBF_PMC_READ_REG_32) 465 + *result = readl(addr); 466 + else 467 + *result = readq(addr); 468 + 469 + return 0; 470 + } 471 + 472 + /* Convenience function for 32-bit reads */ 473 + static int mlxbf_pmc_readl(void __iomem *addr, uint32_t *result) 474 + { 475 + uint64_t read_out; 476 + int status; 477 + 478 + status = mlxbf_pmc_read(addr, MLXBF_PMC_READ_REG_32, &read_out); 479 + if (status) 480 + return status; 481 + *result = (uint32_t)read_out; 482 + 483 + return 0; 484 + } 485 + 486 + /* Calls an SMC to access a performance register */ 487 + static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command, 488 + uint64_t value) 489 + { 490 + struct arm_smccc_res res; 491 + int status, err = 0; 492 + 493 + arm_smccc_smc(command, pmc->sreg_tbl_perf, value, (uintptr_t)addr, 0, 0, 494 + 0, 0, &res); 495 + 496 + status = res.a0; 497 + 498 + switch (status) { 499 + case PSCI_RET_NOT_SUPPORTED: 500 + err = -EINVAL; 501 + break; 502 + case MLXBF_PMC_SMCCC_ACCESS_VIOLATION: 503 + err = -EACCES; 504 + break; 505 + } 506 + 507 + return err; 508 + } 509 + 510 + /* Write to a performance counter */ 511 + static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value) 512 + { 513 + if (pmc->svc_sreg_support) 514 + return mlxbf_pmc_secure_write(addr, command, value); 515 + 516 + if (command == MLXBF_PMC_WRITE_REG_32) 517 + writel(value, addr); 518 + else 519 + writeq(value, addr); 520 + 521 + return 0; 522 + } 523 + 524 + /* Check if the register offset is within the mapped region for the block */ 525 + static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset) 526 + { 527 + if ((offset >= 0) && !(offset % MLXBF_PMC_REG_SIZE) && 528 + (offset + MLXBF_PMC_REG_SIZE <= pmc->block[blk_num].blk_size)) 529 + return true; /* inside the mapped PMC space */ 530 + 531 + return false; 532 + } 533 + 534 + /* Get the event list corresponding to a certain block */ 535 + static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, 536 + int *size) 537 + { 538 + const struct mlxbf_pmc_events *events; 539 + 540 + if (strstr(blk, "tilenet")) { 541 + events = mlxbf_pmc_hnfnet_events; 542 + *size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events); 543 + } else if (strstr(blk, "tile")) { 544 + events = mlxbf_pmc_hnf_events; 545 + *size = ARRAY_SIZE(mlxbf_pmc_hnf_events); 546 + } else if (strstr(blk, "triogen")) { 547 + events = mlxbf_pmc_smgen_events; 548 + *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); 549 + } else if (strstr(blk, "trio")) { 550 + switch (pmc->event_set) { 551 + case MLXBF_PMC_EVENT_SET_BF1: 552 + events = mlxbf_pmc_trio_events_1; 553 + *size = ARRAY_SIZE(mlxbf_pmc_trio_events_1); 554 + break; 555 + case MLXBF_PMC_EVENT_SET_BF2: 556 + events = mlxbf_pmc_trio_events_2; 557 + *size = ARRAY_SIZE(mlxbf_pmc_trio_events_2); 558 + break; 559 + default: 560 + events = NULL; 561 + *size = 0; 562 + break; 563 + } 564 + } else if (strstr(blk, "mss")) { 565 + events = mlxbf_pmc_mss_events; 566 + *size = ARRAY_SIZE(mlxbf_pmc_mss_events); 567 + } else if (strstr(blk, "ecc")) { 568 + events = mlxbf_pmc_ecc_events; 569 + *size = ARRAY_SIZE(mlxbf_pmc_ecc_events); 570 + } else if (strstr(blk, "pcie")) { 571 + events = mlxbf_pmc_pcie_events; 572 + *size = ARRAY_SIZE(mlxbf_pmc_pcie_events); 573 + } else if (strstr(blk, "l3cache")) { 574 + events = mlxbf_pmc_l3c_events; 575 + *size = ARRAY_SIZE(mlxbf_pmc_l3c_events); 576 + } else if (strstr(blk, "gic")) { 577 + events = mlxbf_pmc_smgen_events; 578 + *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); 579 + } else if (strstr(blk, "smmu")) { 580 + events = mlxbf_pmc_smgen_events; 581 + *size = ARRAY_SIZE(mlxbf_pmc_smgen_events); 582 + } else { 583 + events = NULL; 584 + *size = 0; 585 + } 586 + 587 + return events; 588 + } 589 + 590 + /* Get the event number given the name */ 591 + static int mlxbf_pmc_get_event_num(const char *blk, const char *evt) 592 + { 593 + const struct mlxbf_pmc_events *events; 594 + int i, size; 595 + 596 + events = mlxbf_pmc_event_list(blk, &size); 597 + if (!events) 598 + return -EINVAL; 599 + 600 + for (i = 0; i < size; ++i) { 601 + if (!strcmp(evt, events[i].evt_name)) 602 + return events[i].evt_num; 603 + } 604 + 605 + return -ENODEV; 606 + } 607 + 608 + /* Get the event number given the name */ 609 + static char *mlxbf_pmc_get_event_name(const char *blk, int evt) 610 + { 611 + const struct mlxbf_pmc_events *events; 612 + int i, size; 613 + 614 + events = mlxbf_pmc_event_list(blk, &size); 615 + if (!events) 616 + return NULL; 617 + 618 + for (i = 0; i < size; ++i) { 619 + if (evt == events[i].evt_num) 620 + return events[i].evt_name; 621 + } 622 + 623 + return NULL; 624 + } 625 + 626 + /* Method to enable/disable/reset l3cache counters */ 627 + static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset) 628 + { 629 + uint32_t perfcnt_cfg = 0; 630 + 631 + if (enable) 632 + perfcnt_cfg |= MLXBF_PMC_L3C_PERF_CNT_CFG_EN; 633 + if (reset) 634 + perfcnt_cfg |= MLXBF_PMC_L3C_PERF_CNT_CFG_RST; 635 + 636 + return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + 637 + MLXBF_PMC_L3C_PERF_CNT_CFG, 638 + MLXBF_PMC_WRITE_REG_32, perfcnt_cfg); 639 + } 640 + 641 + /* Method to handle l3cache counter programming */ 642 + static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num, 643 + uint32_t evt) 644 + { 645 + uint32_t perfcnt_sel_1 = 0; 646 + uint32_t perfcnt_sel = 0; 647 + uint32_t *wordaddr; 648 + void __iomem *pmcaddr; 649 + int ret; 650 + 651 + /* Disable all counters before programming them */ 652 + if (mlxbf_pmc_config_l3_counters(blk_num, false, false)) 653 + return -EINVAL; 654 + 655 + /* Select appropriate register information */ 656 + switch (cnt_num) { 657 + case 0 ... 3: 658 + pmcaddr = pmc->block[blk_num].mmio_base + 659 + MLXBF_PMC_L3C_PERF_CNT_SEL; 660 + wordaddr = &perfcnt_sel; 661 + break; 662 + case 4: 663 + pmcaddr = pmc->block[blk_num].mmio_base + 664 + MLXBF_PMC_L3C_PERF_CNT_SEL_1; 665 + wordaddr = &perfcnt_sel_1; 666 + break; 667 + default: 668 + return -EINVAL; 669 + } 670 + 671 + ret = mlxbf_pmc_readl(pmcaddr, wordaddr); 672 + if (ret) 673 + return ret; 674 + 675 + switch (cnt_num) { 676 + case 0: 677 + perfcnt_sel &= ~MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_0; 678 + perfcnt_sel |= FIELD_PREP(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_0, 679 + evt); 680 + break; 681 + case 1: 682 + perfcnt_sel &= ~MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_1; 683 + perfcnt_sel |= FIELD_PREP(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_1, 684 + evt); 685 + break; 686 + case 2: 687 + perfcnt_sel &= ~MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_2; 688 + perfcnt_sel |= FIELD_PREP(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_2, 689 + evt); 690 + break; 691 + case 3: 692 + perfcnt_sel &= ~MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_3; 693 + perfcnt_sel |= FIELD_PREP(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_3, 694 + evt); 695 + break; 696 + case 4: 697 + perfcnt_sel_1 &= ~MLXBF_PMC_L3C_PERF_CNT_SEL_1_CNT_4; 698 + perfcnt_sel_1 |= FIELD_PREP(MLXBF_PMC_L3C_PERF_CNT_SEL_1_CNT_4, 699 + evt); 700 + break; 701 + default: 702 + return -EINVAL; 703 + } 704 + 705 + return mlxbf_pmc_write(pmcaddr, MLXBF_PMC_WRITE_REG_32, *wordaddr); 706 + } 707 + 708 + /* Method to program a counter to monitor an event */ 709 + static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num, 710 + uint32_t evt, bool is_l3) 711 + { 712 + uint64_t perfctl, perfevt, perfmon_cfg; 713 + 714 + if (cnt_num >= pmc->block[blk_num].counters) 715 + return -ENODEV; 716 + 717 + if (is_l3) 718 + return mlxbf_pmc_program_l3_counter(blk_num, cnt_num, evt); 719 + 720 + /* Configure the counter */ 721 + perfctl = FIELD_PREP(MLXBF_PMC_PERFCTL_EN0, 1); 722 + perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_EB0, 0); 723 + perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_ETRIG0, 1); 724 + perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_AD0, 0); 725 + perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_ACCM0, 0); 726 + perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_MS0, 0); 727 + perfctl |= FIELD_PREP(MLXBF_PMC_PERFCTL_FM0, 0); 728 + 729 + perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WDATA, perfctl); 730 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, 731 + MLXBF_PMC_PERFCTL); 732 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); 733 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 1); 734 + 735 + if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + 736 + cnt_num * MLXBF_PMC_REG_SIZE, 737 + MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) 738 + return -EFAULT; 739 + 740 + /* Select the event */ 741 + perfevt = FIELD_PREP(MLXBF_PMC_PERFEVT_EVTSEL, evt); 742 + 743 + perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WDATA, perfevt); 744 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, 745 + MLXBF_PMC_PERFEVT); 746 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); 747 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 1); 748 + 749 + if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + 750 + cnt_num * MLXBF_PMC_REG_SIZE, 751 + MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) 752 + return -EFAULT; 753 + 754 + /* Clear the accumulator */ 755 + perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, 756 + MLXBF_PMC_PERFACC0); 757 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); 758 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 1); 759 + 760 + if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + 761 + cnt_num * MLXBF_PMC_REG_SIZE, 762 + MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) 763 + return -EFAULT; 764 + 765 + return 0; 766 + } 767 + 768 + /* Method to handle l3 counter reads */ 769 + static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num, 770 + uint64_t *result) 771 + { 772 + uint32_t perfcnt_low = 0, perfcnt_high = 0; 773 + uint64_t value; 774 + int status = 0; 775 + 776 + status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + 777 + MLXBF_PMC_L3C_PERF_CNT_LOW + 778 + cnt_num * MLXBF_PMC_L3C_REG_SIZE, 779 + &perfcnt_low); 780 + 781 + if (status) 782 + return status; 783 + 784 + status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + 785 + MLXBF_PMC_L3C_PERF_CNT_HIGH + 786 + cnt_num * MLXBF_PMC_L3C_REG_SIZE, 787 + &perfcnt_high); 788 + 789 + if (status) 790 + return status; 791 + 792 + value = perfcnt_high; 793 + value = value << 32; 794 + value |= perfcnt_low; 795 + *result = value; 796 + 797 + return 0; 798 + } 799 + 800 + /* Method to read the counter value */ 801 + static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3, 802 + uint64_t *result) 803 + { 804 + uint32_t perfcfg_offset, perfval_offset; 805 + uint64_t perfmon_cfg; 806 + int status; 807 + 808 + if (cnt_num >= pmc->block[blk_num].counters) 809 + return -EINVAL; 810 + 811 + if (is_l3) 812 + return mlxbf_pmc_read_l3_counter(blk_num, cnt_num, result); 813 + 814 + perfcfg_offset = cnt_num * MLXBF_PMC_REG_SIZE; 815 + perfval_offset = perfcfg_offset + 816 + pmc->block[blk_num].counters * MLXBF_PMC_REG_SIZE; 817 + 818 + /* Set counter in "read" mode */ 819 + perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, 820 + MLXBF_PMC_PERFACC0); 821 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); 822 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 0); 823 + 824 + status = mlxbf_pmc_write(pmc->block[blk_num].mmio_base + perfcfg_offset, 825 + MLXBF_PMC_WRITE_REG_64, perfmon_cfg); 826 + 827 + if (status) 828 + return status; 829 + 830 + /* Get the counter value */ 831 + return mlxbf_pmc_read(pmc->block[blk_num].mmio_base + perfval_offset, 832 + MLXBF_PMC_READ_REG_64, result); 833 + } 834 + 835 + /* Method to read L3 block event */ 836 + static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num, 837 + uint64_t *result) 838 + { 839 + uint32_t perfcnt_sel = 0, perfcnt_sel_1 = 0; 840 + uint32_t *wordaddr; 841 + void __iomem *pmcaddr; 842 + uint64_t evt; 843 + 844 + /* Select appropriate register information */ 845 + switch (cnt_num) { 846 + case 0 ... 3: 847 + pmcaddr = pmc->block[blk_num].mmio_base + 848 + MLXBF_PMC_L3C_PERF_CNT_SEL; 849 + wordaddr = &perfcnt_sel; 850 + break; 851 + case 4: 852 + pmcaddr = pmc->block[blk_num].mmio_base + 853 + MLXBF_PMC_L3C_PERF_CNT_SEL_1; 854 + wordaddr = &perfcnt_sel_1; 855 + break; 856 + default: 857 + return -EINVAL; 858 + } 859 + 860 + if (mlxbf_pmc_readl(pmcaddr, wordaddr)) 861 + return -EINVAL; 862 + 863 + /* Read from appropriate register field for the counter */ 864 + switch (cnt_num) { 865 + case 0: 866 + evt = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_0, perfcnt_sel); 867 + break; 868 + case 1: 869 + evt = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_1, perfcnt_sel); 870 + break; 871 + case 2: 872 + evt = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_2, perfcnt_sel); 873 + break; 874 + case 3: 875 + evt = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_SEL_CNT_3, perfcnt_sel); 876 + break; 877 + case 4: 878 + evt = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_SEL_1_CNT_4, 879 + perfcnt_sel_1); 880 + break; 881 + default: 882 + return -EINVAL; 883 + } 884 + *result = evt; 885 + 886 + return 0; 887 + } 888 + 889 + /* Method to find the event currently being monitored by a counter */ 890 + static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, 891 + uint64_t *result) 892 + { 893 + uint32_t perfcfg_offset, perfval_offset; 894 + uint64_t perfmon_cfg, perfevt, perfctl; 895 + 896 + if (cnt_num >= pmc->block[blk_num].counters) 897 + return -EINVAL; 898 + 899 + if (is_l3) 900 + return mlxbf_pmc_read_l3_event(blk_num, cnt_num, result); 901 + 902 + perfcfg_offset = cnt_num * MLXBF_PMC_REG_SIZE; 903 + perfval_offset = perfcfg_offset + 904 + pmc->block[blk_num].counters * MLXBF_PMC_REG_SIZE; 905 + 906 + /* Set counter in "read" mode */ 907 + perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, 908 + MLXBF_PMC_PERFCTL); 909 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); 910 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 0); 911 + 912 + if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + perfcfg_offset, 913 + MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) 914 + return -EFAULT; 915 + 916 + /* Check if the counter is enabled */ 917 + 918 + if (mlxbf_pmc_read(pmc->block[blk_num].mmio_base + perfval_offset, 919 + MLXBF_PMC_READ_REG_64, &perfctl)) 920 + return -EFAULT; 921 + 922 + if (!FIELD_GET(MLXBF_PMC_PERFCTL_EN0, perfctl)) 923 + return -EINVAL; 924 + 925 + /* Set counter in "read" mode */ 926 + perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, 927 + MLXBF_PMC_PERFEVT); 928 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); 929 + perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 0); 930 + 931 + if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + perfcfg_offset, 932 + MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) 933 + return -EFAULT; 934 + 935 + /* Get the event number */ 936 + if (mlxbf_pmc_read(pmc->block[blk_num].mmio_base + perfval_offset, 937 + MLXBF_PMC_READ_REG_64, &perfevt)) 938 + return -EFAULT; 939 + 940 + *result = FIELD_GET(MLXBF_PMC_PERFEVT_EVTSEL, perfevt); 941 + 942 + return 0; 943 + } 944 + 945 + /* Method to read a register */ 946 + static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result) 947 + { 948 + uint32_t ecc_out; 949 + 950 + if (strstr(pmc->block_name[blk_num], "ecc")) { 951 + if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset, 952 + &ecc_out)) 953 + return -EFAULT; 954 + 955 + *result = ecc_out; 956 + return 0; 957 + } 958 + 959 + if (mlxbf_pmc_valid_range(blk_num, offset)) 960 + return mlxbf_pmc_read(pmc->block[blk_num].mmio_base + offset, 961 + MLXBF_PMC_READ_REG_64, result); 962 + 963 + return -EINVAL; 964 + } 965 + 966 + /* Method to write to a register */ 967 + static int mlxbf_pmc_write_reg(int blk_num, uint32_t offset, uint64_t data) 968 + { 969 + if (strstr(pmc->block_name[blk_num], "ecc")) { 970 + return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset, 971 + MLXBF_PMC_WRITE_REG_32, data); 972 + } 973 + 974 + if (mlxbf_pmc_valid_range(blk_num, offset)) 975 + return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset, 976 + MLXBF_PMC_WRITE_REG_64, data); 977 + 978 + return -EINVAL; 979 + } 980 + 981 + /* Show function for "counter" sysfs files */ 982 + static ssize_t mlxbf_pmc_counter_show(struct device *dev, 983 + struct device_attribute *attr, char *buf) 984 + { 985 + struct mlxbf_pmc_attribute *attr_counter = container_of( 986 + attr, struct mlxbf_pmc_attribute, dev_attr); 987 + int blk_num, cnt_num, offset; 988 + bool is_l3 = false; 989 + uint64_t value; 990 + 991 + blk_num = attr_counter->nr; 992 + cnt_num = attr_counter->index; 993 + 994 + if (strstr(pmc->block_name[blk_num], "l3cache")) 995 + is_l3 = true; 996 + 997 + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) { 998 + if (mlxbf_pmc_read_counter(blk_num, cnt_num, is_l3, &value)) 999 + return -EINVAL; 1000 + } else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER) { 1001 + offset = mlxbf_pmc_get_event_num(pmc->block_name[blk_num], 1002 + attr->attr.name); 1003 + if (offset < 0) 1004 + return -EINVAL; 1005 + if (mlxbf_pmc_read_reg(blk_num, offset, &value)) 1006 + return -EINVAL; 1007 + } else 1008 + return -EINVAL; 1009 + 1010 + return sprintf(buf, "0x%llx\n", value); 1011 + } 1012 + 1013 + /* Store function for "counter" sysfs files */ 1014 + static ssize_t mlxbf_pmc_counter_store(struct device *dev, 1015 + struct device_attribute *attr, 1016 + const char *buf, size_t count) 1017 + { 1018 + struct mlxbf_pmc_attribute *attr_counter = container_of( 1019 + attr, struct mlxbf_pmc_attribute, dev_attr); 1020 + int blk_num, cnt_num, offset, err, data; 1021 + bool is_l3 = false; 1022 + uint64_t evt_num; 1023 + 1024 + blk_num = attr_counter->nr; 1025 + cnt_num = attr_counter->index; 1026 + 1027 + err = kstrtoint(buf, 0, &data); 1028 + if (err < 0) 1029 + return err; 1030 + 1031 + /* Allow non-zero writes only to the ecc regs */ 1032 + if (!(strstr(pmc->block_name[blk_num], "ecc")) && data) 1033 + return -EINVAL; 1034 + 1035 + /* Do not allow writes to the L3C regs */ 1036 + if (strstr(pmc->block_name[blk_num], "l3cache")) 1037 + return -EINVAL; 1038 + 1039 + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) { 1040 + err = mlxbf_pmc_read_event(blk_num, cnt_num, is_l3, &evt_num); 1041 + if (err) 1042 + return err; 1043 + err = mlxbf_pmc_program_counter(blk_num, cnt_num, evt_num, 1044 + is_l3); 1045 + if (err) 1046 + return err; 1047 + } else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER) { 1048 + offset = mlxbf_pmc_get_event_num(pmc->block_name[blk_num], 1049 + attr->attr.name); 1050 + if (offset < 0) 1051 + return -EINVAL; 1052 + err = mlxbf_pmc_write_reg(blk_num, offset, data); 1053 + if (err) 1054 + return err; 1055 + } else 1056 + return -EINVAL; 1057 + 1058 + return count; 1059 + } 1060 + 1061 + /* Show function for "event" sysfs files */ 1062 + static ssize_t mlxbf_pmc_event_show(struct device *dev, 1063 + struct device_attribute *attr, char *buf) 1064 + { 1065 + struct mlxbf_pmc_attribute *attr_event = container_of( 1066 + attr, struct mlxbf_pmc_attribute, dev_attr); 1067 + int blk_num, cnt_num, err; 1068 + bool is_l3 = false; 1069 + uint64_t evt_num; 1070 + char *evt_name; 1071 + 1072 + blk_num = attr_event->nr; 1073 + cnt_num = attr_event->index; 1074 + 1075 + if (strstr(pmc->block_name[blk_num], "l3cache")) 1076 + is_l3 = true; 1077 + 1078 + err = mlxbf_pmc_read_event(blk_num, cnt_num, is_l3, &evt_num); 1079 + if (err) 1080 + return sprintf(buf, "No event being monitored\n"); 1081 + 1082 + evt_name = mlxbf_pmc_get_event_name(pmc->block_name[blk_num], evt_num); 1083 + if (!evt_name) 1084 + return -EINVAL; 1085 + 1086 + return sprintf(buf, "0x%llx: %s\n", evt_num, evt_name); 1087 + } 1088 + 1089 + /* Store function for "event" sysfs files */ 1090 + static ssize_t mlxbf_pmc_event_store(struct device *dev, 1091 + struct device_attribute *attr, 1092 + const char *buf, size_t count) 1093 + { 1094 + struct mlxbf_pmc_attribute *attr_event = container_of( 1095 + attr, struct mlxbf_pmc_attribute, dev_attr); 1096 + int blk_num, cnt_num, evt_num, err; 1097 + bool is_l3 = false; 1098 + 1099 + blk_num = attr_event->nr; 1100 + cnt_num = attr_event->index; 1101 + 1102 + if (isalpha(buf[0])) { 1103 + evt_num = mlxbf_pmc_get_event_num(pmc->block_name[blk_num], 1104 + buf); 1105 + if (evt_num < 0) 1106 + return -EINVAL; 1107 + } else { 1108 + err = kstrtoint(buf, 0, &evt_num); 1109 + if (err < 0) 1110 + return err; 1111 + } 1112 + 1113 + if (strstr(pmc->block_name[blk_num], "l3cache")) 1114 + is_l3 = true; 1115 + 1116 + err = mlxbf_pmc_program_counter(blk_num, cnt_num, evt_num, is_l3); 1117 + if (err) 1118 + return err; 1119 + 1120 + return count; 1121 + } 1122 + 1123 + /* Show function for "event_list" sysfs files */ 1124 + static ssize_t mlxbf_pmc_event_list_show(struct device *dev, 1125 + struct device_attribute *attr, 1126 + char *buf) 1127 + { 1128 + struct mlxbf_pmc_attribute *attr_event_list = container_of( 1129 + attr, struct mlxbf_pmc_attribute, dev_attr); 1130 + int blk_num, i, size, len = 0, ret = 0; 1131 + const struct mlxbf_pmc_events *events; 1132 + char e_info[MLXBF_PMC_EVENT_INFO_LEN]; 1133 + 1134 + blk_num = attr_event_list->nr; 1135 + 1136 + events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &size); 1137 + if (!events) 1138 + return -EINVAL; 1139 + 1140 + for (i = 0, buf[0] = '\0'; i < size; ++i) { 1141 + len += sprintf(e_info, "0x%x: %s\n", events[i].evt_num, 1142 + events[i].evt_name); 1143 + if (len > PAGE_SIZE) 1144 + break; 1145 + strcat(buf, e_info); 1146 + ret = len; 1147 + } 1148 + 1149 + return ret; 1150 + } 1151 + 1152 + /* Show function for "enable" sysfs files - only for l3cache */ 1153 + static ssize_t mlxbf_pmc_enable_show(struct device *dev, 1154 + struct device_attribute *attr, char *buf) 1155 + { 1156 + struct mlxbf_pmc_attribute *attr_enable = container_of( 1157 + attr, struct mlxbf_pmc_attribute, dev_attr); 1158 + uint32_t perfcnt_cfg; 1159 + int blk_num, value; 1160 + 1161 + blk_num = attr_enable->nr; 1162 + 1163 + if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + 1164 + MLXBF_PMC_L3C_PERF_CNT_CFG, 1165 + &perfcnt_cfg)) 1166 + return -EINVAL; 1167 + 1168 + value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg); 1169 + 1170 + return sprintf(buf, "%d\n", value); 1171 + } 1172 + 1173 + /* Store function for "enable" sysfs files - only for l3cache */ 1174 + static ssize_t mlxbf_pmc_enable_store(struct device *dev, 1175 + struct device_attribute *attr, 1176 + const char *buf, size_t count) 1177 + { 1178 + struct mlxbf_pmc_attribute *attr_enable = container_of( 1179 + attr, struct mlxbf_pmc_attribute, dev_attr); 1180 + int err, en, blk_num; 1181 + 1182 + blk_num = attr_enable->nr; 1183 + 1184 + err = kstrtoint(buf, 0, &en); 1185 + if (err < 0) 1186 + return err; 1187 + 1188 + if (!en) { 1189 + err = mlxbf_pmc_config_l3_counters(blk_num, false, false); 1190 + if (err) 1191 + return err; 1192 + } else if (en == 1) { 1193 + err = mlxbf_pmc_config_l3_counters(blk_num, false, true); 1194 + if (err) 1195 + return err; 1196 + err = mlxbf_pmc_config_l3_counters(blk_num, true, false); 1197 + if (err) 1198 + return err; 1199 + } else 1200 + return -EINVAL; 1201 + 1202 + return count; 1203 + } 1204 + 1205 + /* Populate attributes for blocks with counters to monitor performance */ 1206 + static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num) 1207 + { 1208 + struct mlxbf_pmc_attribute *attr; 1209 + int i = 0, j = 0; 1210 + 1211 + /* "event_list" sysfs to list events supported by the block */ 1212 + attr = &pmc->block[blk_num].attr_event_list; 1213 + attr->dev_attr.attr.mode = 0444; 1214 + attr->dev_attr.show = mlxbf_pmc_event_list_show; 1215 + attr->nr = blk_num; 1216 + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "event_list"); 1217 + pmc->block[blk_num].block_attr[i] = &attr->dev_attr.attr; 1218 + attr = NULL; 1219 + 1220 + /* "enable" sysfs to start/stop the counters. Only in L3C blocks */ 1221 + if (strstr(pmc->block_name[blk_num], "l3cache")) { 1222 + attr = &pmc->block[blk_num].attr_enable; 1223 + attr->dev_attr.attr.mode = 0644; 1224 + attr->dev_attr.show = mlxbf_pmc_enable_show; 1225 + attr->dev_attr.store = mlxbf_pmc_enable_store; 1226 + attr->nr = blk_num; 1227 + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, 1228 + "enable"); 1229 + pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr; 1230 + attr = NULL; 1231 + } 1232 + 1233 + pmc->block[blk_num].attr_counter = devm_kcalloc( 1234 + dev, pmc->block[blk_num].counters, 1235 + sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); 1236 + if (!pmc->block[blk_num].attr_counter) 1237 + return -ENOMEM; 1238 + 1239 + pmc->block[blk_num].attr_event = devm_kcalloc( 1240 + dev, pmc->block[blk_num].counters, 1241 + sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); 1242 + if (!pmc->block[blk_num].attr_event) 1243 + return -ENOMEM; 1244 + 1245 + /* "eventX" and "counterX" sysfs to program and read counter values */ 1246 + for (j = 0; j < pmc->block[blk_num].counters; ++j) { 1247 + attr = &pmc->block[blk_num].attr_counter[j]; 1248 + attr->dev_attr.attr.mode = 0644; 1249 + attr->dev_attr.show = mlxbf_pmc_counter_show; 1250 + attr->dev_attr.store = mlxbf_pmc_counter_store; 1251 + attr->index = j; 1252 + attr->nr = blk_num; 1253 + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, 1254 + "counter%d", j); 1255 + pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr; 1256 + attr = NULL; 1257 + 1258 + attr = &pmc->block[blk_num].attr_event[j]; 1259 + attr->dev_attr.attr.mode = 0644; 1260 + attr->dev_attr.show = mlxbf_pmc_event_show; 1261 + attr->dev_attr.store = mlxbf_pmc_event_store; 1262 + attr->index = j; 1263 + attr->nr = blk_num; 1264 + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, 1265 + "event%d", j); 1266 + pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr; 1267 + attr = NULL; 1268 + } 1269 + 1270 + return 0; 1271 + } 1272 + 1273 + /* Populate attributes for blocks with registers to monitor performance */ 1274 + static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num) 1275 + { 1276 + struct mlxbf_pmc_attribute *attr; 1277 + const struct mlxbf_pmc_events *events; 1278 + int i = 0, j = 0; 1279 + 1280 + events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &j); 1281 + if (!events) 1282 + return -EINVAL; 1283 + 1284 + pmc->block[blk_num].attr_event = devm_kcalloc( 1285 + dev, j, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL); 1286 + if (!pmc->block[blk_num].attr_event) 1287 + return -ENOMEM; 1288 + 1289 + while (j > 0) { 1290 + --j; 1291 + attr = &pmc->block[blk_num].attr_event[j]; 1292 + attr->dev_attr.attr.mode = 0644; 1293 + attr->dev_attr.show = mlxbf_pmc_counter_show; 1294 + attr->dev_attr.store = mlxbf_pmc_counter_store; 1295 + attr->nr = blk_num; 1296 + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, 1297 + events[j].evt_name); 1298 + pmc->block[blk_num].block_attr[i] = &attr->dev_attr.attr; 1299 + attr = NULL; 1300 + i++; 1301 + } 1302 + 1303 + return 0; 1304 + } 1305 + 1306 + /* Helper to create the bfperf sysfs sub-directories and files */ 1307 + static int mlxbf_pmc_create_groups(struct device *dev, int blk_num) 1308 + { 1309 + int err; 1310 + 1311 + /* Populate attributes based on counter type */ 1312 + if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_COUNTER) 1313 + err = mlxbf_pmc_init_perftype_counter(dev, blk_num); 1314 + else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER) 1315 + err = mlxbf_pmc_init_perftype_reg(dev, blk_num); 1316 + else 1317 + err = -EINVAL; 1318 + 1319 + if (err) 1320 + return err; 1321 + 1322 + /* Add a new attribute_group for the block */ 1323 + pmc->block[blk_num].block_attr_grp.attrs = pmc->block[blk_num].block_attr; 1324 + pmc->block[blk_num].block_attr_grp.name = devm_kasprintf( 1325 + dev, GFP_KERNEL, pmc->block_name[blk_num]); 1326 + pmc->groups[blk_num] = &pmc->block[blk_num].block_attr_grp; 1327 + 1328 + return 0; 1329 + } 1330 + 1331 + static bool mlxbf_pmc_guid_match(const guid_t *guid, 1332 + const struct arm_smccc_res *res) 1333 + { 1334 + guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16, res->a2, 1335 + res->a2 >> 8, res->a2 >> 16, res->a2 >> 24, 1336 + res->a3, res->a3 >> 8, res->a3 >> 16, 1337 + res->a3 >> 24); 1338 + 1339 + return guid_equal(guid, &id); 1340 + } 1341 + 1342 + /* Helper to map the Performance Counters from the varios blocks */ 1343 + static int mlxbf_pmc_map_counters(struct device *dev) 1344 + { 1345 + uint64_t info[MLXBF_PMC_INFO_SZ]; 1346 + int i, tile_num, ret; 1347 + 1348 + for (i = 0; i < pmc->total_blocks; ++i) { 1349 + if (strstr(pmc->block_name[i], "tile")) { 1350 + ret = sscanf(pmc->block_name[i], "tile%d", &tile_num); 1351 + if (ret < 0) 1352 + return ret; 1353 + 1354 + if (tile_num >= pmc->tile_count) 1355 + continue; 1356 + } 1357 + ret = device_property_read_u64_array(dev, pmc->block_name[i], 1358 + info, MLXBF_PMC_INFO_SZ); 1359 + if (ret) 1360 + return ret; 1361 + 1362 + /* 1363 + * Do not remap if the proper SMC calls are supported, 1364 + * since the SMC calls expect physical addresses. 1365 + */ 1366 + if (pmc->svc_sreg_support) 1367 + pmc->block[i].mmio_base = (void __iomem *)info[0]; 1368 + else 1369 + pmc->block[i].mmio_base = 1370 + devm_ioremap(dev, info[0], info[1]); 1371 + 1372 + pmc->block[i].blk_size = info[1]; 1373 + pmc->block[i].counters = info[2]; 1374 + pmc->block[i].type = info[3]; 1375 + 1376 + if (IS_ERR(pmc->block[i].mmio_base)) 1377 + return PTR_ERR(pmc->block[i].mmio_base); 1378 + 1379 + ret = mlxbf_pmc_create_groups(dev, i); 1380 + if (ret) 1381 + return ret; 1382 + } 1383 + 1384 + return 0; 1385 + } 1386 + 1387 + static int mlxbf_pmc_probe(struct platform_device *pdev) 1388 + { 1389 + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); 1390 + const char *hid = acpi_device_hid(acpi_dev); 1391 + struct device *dev = &pdev->dev; 1392 + struct arm_smccc_res res; 1393 + guid_t guid; 1394 + int ret; 1395 + 1396 + /* Ensure we have the UUID we expect for this service. */ 1397 + arm_smccc_smc(MLXBF_PMC_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); 1398 + guid_parse(mlxbf_pmc_svc_uuid_str, &guid); 1399 + if (!mlxbf_pmc_guid_match(&guid, &res)) 1400 + return -ENODEV; 1401 + 1402 + pmc = devm_kzalloc(dev, sizeof(struct mlxbf_pmc_context), GFP_KERNEL); 1403 + if (!pmc) 1404 + return -ENOMEM; 1405 + 1406 + /* 1407 + * ACPI indicates whether we use SMCs to access registers or not. 1408 + * If sreg_tbl_perf is not present, just assume we're not using SMCs. 1409 + */ 1410 + ret = device_property_read_u32(dev, "sec_reg_block", 1411 + &pmc->sreg_tbl_perf); 1412 + if (ret) { 1413 + pmc->svc_sreg_support = false; 1414 + } else { 1415 + /* 1416 + * Check service version to see if we actually do support the 1417 + * needed SMCs. If we have the calls we need, mark support for 1418 + * them in the pmc struct. 1419 + */ 1420 + arm_smccc_smc(MLXBF_PMC_SIP_SVC_VERSION, 0, 0, 0, 0, 0, 0, 0, 1421 + &res); 1422 + if (res.a0 == MLXBF_PMC_SVC_REQ_MAJOR && 1423 + res.a1 >= MLXBF_PMC_SVC_MIN_MINOR) 1424 + pmc->svc_sreg_support = true; 1425 + else 1426 + return -EINVAL; 1427 + } 1428 + 1429 + if (!strcmp(hid, "MLNXBFD0")) 1430 + pmc->event_set = MLXBF_PMC_EVENT_SET_BF1; 1431 + else if (!strcmp(hid, "MLNXBFD1")) 1432 + pmc->event_set = MLXBF_PMC_EVENT_SET_BF2; 1433 + else 1434 + return -ENODEV; 1435 + 1436 + ret = device_property_read_u32(dev, "block_num", &pmc->total_blocks); 1437 + if (ret) 1438 + return ret; 1439 + 1440 + ret = device_property_read_string_array(dev, "block_name", 1441 + pmc->block_name, 1442 + pmc->total_blocks); 1443 + if (ret != pmc->total_blocks) 1444 + return -EFAULT; 1445 + 1446 + ret = device_property_read_u32(dev, "tile_num", &pmc->tile_count); 1447 + if (ret) 1448 + return ret; 1449 + 1450 + pmc->pdev = pdev; 1451 + 1452 + ret = mlxbf_pmc_map_counters(dev); 1453 + if (ret) 1454 + return ret; 1455 + 1456 + pmc->hwmon_dev = devm_hwmon_device_register_with_groups( 1457 + dev, "bfperf", pmc, pmc->groups); 1458 + platform_set_drvdata(pdev, pmc); 1459 + 1460 + return 0; 1461 + } 1462 + 1463 + static const struct acpi_device_id mlxbf_pmc_acpi_ids[] = { { "MLNXBFD0", 0 }, 1464 + { "MLNXBFD1", 0 }, 1465 + {}, }; 1466 + 1467 + MODULE_DEVICE_TABLE(acpi, mlxbf_pmc_acpi_ids); 1468 + static struct platform_driver pmc_driver = { 1469 + .driver = { .name = "mlxbf-pmc", 1470 + .acpi_match_table = ACPI_PTR(mlxbf_pmc_acpi_ids), }, 1471 + .probe = mlxbf_pmc_probe, 1472 + }; 1473 + 1474 + module_platform_driver(pmc_driver); 1475 + 1476 + MODULE_AUTHOR("Shravan Kumar Ramani <sramani@mellanox.com>"); 1477 + MODULE_DESCRIPTION("Mellanox PMC driver"); 1478 + MODULE_LICENSE("Dual BSD/GPL");
+59
drivers/platform/surface/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Microsoft Surface Platform-Specific Drivers 4 + # 5 + 6 + menuconfig SURFACE_PLATFORMS 7 + bool "Microsoft Surface Platform-Specific Device Drivers" 8 + default y 9 + help 10 + Say Y here to get to see options for platform-specific device drivers 11 + for Microsoft Surface devices. This option alone does not add any 12 + kernel code. 13 + 14 + If you say N, all options in this submenu will be skipped and disabled. 15 + 16 + if SURFACE_PLATFORMS 17 + 18 + config SURFACE3_WMI 19 + tristate "Surface 3 WMI Driver" 20 + depends on ACPI_WMI 21 + depends on DMI 22 + depends on INPUT 23 + depends on SPI 24 + help 25 + Say Y here if you have a Surface 3. 26 + 27 + To compile this driver as a module, choose M here: the module will 28 + be called surface3-wmi. 29 + 30 + config SURFACE_3_BUTTON 31 + tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet" 32 + depends on ACPI && KEYBOARD_GPIO && I2C 33 + help 34 + This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. 35 + 36 + config SURFACE_3_POWER_OPREGION 37 + tristate "Surface 3 battery platform operation region support" 38 + depends on ACPI && I2C 39 + help 40 + This driver provides support for ACPI operation 41 + region of the Surface 3 battery platform driver. 42 + 43 + config SURFACE_GPE 44 + tristate "Surface GPE/Lid Support Driver" 45 + depends on ACPI 46 + depends on DMI 47 + help 48 + This driver marks the GPEs related to the ACPI lid device found on 49 + Microsoft Surface devices as wakeup sources and prepares them 50 + accordingly. It is required on those devices to allow wake-ups from 51 + suspend by opening the lid. 52 + 53 + config SURFACE_PRO3_BUTTON 54 + tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet" 55 + depends on ACPI && INPUT 56 + help 57 + This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. 58 + 59 + endif # SURFACE_PLATFORMS
+11
drivers/platform/surface/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for linux/drivers/platform/surface 4 + # Microsoft Surface Platform-Specific Drivers 5 + # 6 + 7 + obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o 8 + obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o 9 + obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o 10 + obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o 11 + obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
+321
drivers/platform/surface/surface_gpe.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Surface GPE/Lid driver to enable wakeup from suspend via the lid by 4 + * properly configuring the respective GPEs. Required for wakeup via lid on 5 + * newer Intel-based Microsoft Surface devices. 6 + * 7 + * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com> 8 + */ 9 + 10 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 + 12 + #include <linux/acpi.h> 13 + #include <linux/dmi.h> 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + 18 + /* 19 + * Note: The GPE numbers for the lid devices found below have been obtained 20 + * from ACPI/the DSDT table, specifically from the GPE handler for the 21 + * lid. 22 + */ 23 + 24 + static const struct property_entry lid_device_props_l17[] = { 25 + PROPERTY_ENTRY_U32("gpe", 0x17), 26 + {}, 27 + }; 28 + 29 + static const struct property_entry lid_device_props_l4D[] = { 30 + PROPERTY_ENTRY_U32("gpe", 0x4D), 31 + {}, 32 + }; 33 + 34 + static const struct property_entry lid_device_props_l4F[] = { 35 + PROPERTY_ENTRY_U32("gpe", 0x4F), 36 + {}, 37 + }; 38 + 39 + static const struct property_entry lid_device_props_l57[] = { 40 + PROPERTY_ENTRY_U32("gpe", 0x57), 41 + {}, 42 + }; 43 + 44 + /* 45 + * Note: When changing this, don't forget to check that the MODULE_ALIAS below 46 + * still fits. 47 + */ 48 + static const struct dmi_system_id dmi_lid_device_table[] = { 49 + { 50 + .ident = "Surface Pro 4", 51 + .matches = { 52 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 53 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), 54 + }, 55 + .driver_data = (void *)lid_device_props_l17, 56 + }, 57 + { 58 + .ident = "Surface Pro 5", 59 + .matches = { 60 + /* 61 + * We match for SKU here due to generic product name 62 + * "Surface Pro". 63 + */ 64 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 65 + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), 66 + }, 67 + .driver_data = (void *)lid_device_props_l4F, 68 + }, 69 + { 70 + .ident = "Surface Pro 5 (LTE)", 71 + .matches = { 72 + /* 73 + * We match for SKU here due to generic product name 74 + * "Surface Pro" 75 + */ 76 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 77 + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), 78 + }, 79 + .driver_data = (void *)lid_device_props_l4F, 80 + }, 81 + { 82 + .ident = "Surface Pro 6", 83 + .matches = { 84 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 85 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), 86 + }, 87 + .driver_data = (void *)lid_device_props_l4F, 88 + }, 89 + { 90 + .ident = "Surface Pro 7", 91 + .matches = { 92 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 93 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 7"), 94 + }, 95 + .driver_data = (void *)lid_device_props_l4D, 96 + }, 97 + { 98 + .ident = "Surface Book 1", 99 + .matches = { 100 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 101 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), 102 + }, 103 + .driver_data = (void *)lid_device_props_l17, 104 + }, 105 + { 106 + .ident = "Surface Book 2", 107 + .matches = { 108 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 109 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), 110 + }, 111 + .driver_data = (void *)lid_device_props_l17, 112 + }, 113 + { 114 + .ident = "Surface Book 3", 115 + .matches = { 116 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 117 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 3"), 118 + }, 119 + .driver_data = (void *)lid_device_props_l4D, 120 + }, 121 + { 122 + .ident = "Surface Laptop 1", 123 + .matches = { 124 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 125 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), 126 + }, 127 + .driver_data = (void *)lid_device_props_l57, 128 + }, 129 + { 130 + .ident = "Surface Laptop 2", 131 + .matches = { 132 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 133 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), 134 + }, 135 + .driver_data = (void *)lid_device_props_l57, 136 + }, 137 + { 138 + .ident = "Surface Laptop 3 (Intel 13\")", 139 + .matches = { 140 + /* 141 + * We match for SKU here due to different variants: The 142 + * AMD (15") version does not rely on GPEs. 143 + */ 144 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 145 + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1867:1868"), 146 + }, 147 + .driver_data = (void *)lid_device_props_l4D, 148 + }, 149 + { 150 + .ident = "Surface Laptop 3 (Intel 15\")", 151 + .matches = { 152 + /* 153 + * We match for SKU here due to different variants: The 154 + * AMD (15") version does not rely on GPEs. 155 + */ 156 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 157 + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1872"), 158 + }, 159 + .driver_data = (void *)lid_device_props_l4D, 160 + }, 161 + { } 162 + }; 163 + 164 + struct surface_lid_device { 165 + u32 gpe_number; 166 + }; 167 + 168 + static int surface_lid_enable_wakeup(struct device *dev, bool enable) 169 + { 170 + const struct surface_lid_device *lid = dev_get_drvdata(dev); 171 + int action = enable ? ACPI_GPE_ENABLE : ACPI_GPE_DISABLE; 172 + acpi_status status; 173 + 174 + status = acpi_set_gpe_wake_mask(NULL, lid->gpe_number, action); 175 + if (ACPI_FAILURE(status)) { 176 + dev_err(dev, "failed to set GPE wake mask: %s\n", 177 + acpi_format_exception(status)); 178 + return -EINVAL; 179 + } 180 + 181 + return 0; 182 + } 183 + 184 + static int surface_gpe_suspend(struct device *dev) 185 + { 186 + return surface_lid_enable_wakeup(dev, true); 187 + } 188 + 189 + static int surface_gpe_resume(struct device *dev) 190 + { 191 + return surface_lid_enable_wakeup(dev, false); 192 + } 193 + 194 + static SIMPLE_DEV_PM_OPS(surface_gpe_pm, surface_gpe_suspend, surface_gpe_resume); 195 + 196 + static int surface_gpe_probe(struct platform_device *pdev) 197 + { 198 + struct surface_lid_device *lid; 199 + u32 gpe_number; 200 + acpi_status status; 201 + int ret; 202 + 203 + ret = device_property_read_u32(&pdev->dev, "gpe", &gpe_number); 204 + if (ret) { 205 + dev_err(&pdev->dev, "failed to read 'gpe' property: %d\n", ret); 206 + return ret; 207 + } 208 + 209 + lid = devm_kzalloc(&pdev->dev, sizeof(*lid), GFP_KERNEL); 210 + if (!lid) 211 + return -ENOMEM; 212 + 213 + lid->gpe_number = gpe_number; 214 + platform_set_drvdata(pdev, lid); 215 + 216 + status = acpi_mark_gpe_for_wake(NULL, gpe_number); 217 + if (ACPI_FAILURE(status)) { 218 + dev_err(&pdev->dev, "failed to mark GPE for wake: %s\n", 219 + acpi_format_exception(status)); 220 + return -EINVAL; 221 + } 222 + 223 + status = acpi_enable_gpe(NULL, gpe_number); 224 + if (ACPI_FAILURE(status)) { 225 + dev_err(&pdev->dev, "failed to enable GPE: %s\n", 226 + acpi_format_exception(status)); 227 + return -EINVAL; 228 + } 229 + 230 + ret = surface_lid_enable_wakeup(&pdev->dev, false); 231 + if (ret) 232 + acpi_disable_gpe(NULL, gpe_number); 233 + 234 + return ret; 235 + } 236 + 237 + static int surface_gpe_remove(struct platform_device *pdev) 238 + { 239 + struct surface_lid_device *lid = dev_get_drvdata(&pdev->dev); 240 + 241 + /* restore default behavior without this module */ 242 + surface_lid_enable_wakeup(&pdev->dev, false); 243 + acpi_disable_gpe(NULL, lid->gpe_number); 244 + 245 + return 0; 246 + } 247 + 248 + static struct platform_driver surface_gpe_driver = { 249 + .probe = surface_gpe_probe, 250 + .remove = surface_gpe_remove, 251 + .driver = { 252 + .name = "surface_gpe", 253 + .pm = &surface_gpe_pm, 254 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 255 + }, 256 + }; 257 + 258 + static struct platform_device *surface_gpe_device; 259 + 260 + static int __init surface_gpe_init(void) 261 + { 262 + const struct dmi_system_id *match; 263 + struct platform_device *pdev; 264 + struct fwnode_handle *fwnode; 265 + int status; 266 + 267 + match = dmi_first_match(dmi_lid_device_table); 268 + if (!match) { 269 + pr_info("no compatible Microsoft Surface device found, exiting\n"); 270 + return -ENODEV; 271 + } 272 + 273 + status = platform_driver_register(&surface_gpe_driver); 274 + if (status) 275 + return status; 276 + 277 + fwnode = fwnode_create_software_node(match->driver_data, NULL); 278 + if (IS_ERR(fwnode)) { 279 + status = PTR_ERR(fwnode); 280 + goto err_node; 281 + } 282 + 283 + pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE); 284 + if (!pdev) { 285 + status = -ENOMEM; 286 + goto err_alloc; 287 + } 288 + 289 + pdev->dev.fwnode = fwnode; 290 + 291 + status = platform_device_add(pdev); 292 + if (status) 293 + goto err_add; 294 + 295 + surface_gpe_device = pdev; 296 + return 0; 297 + 298 + err_add: 299 + platform_device_put(pdev); 300 + err_alloc: 301 + fwnode_remove_software_node(fwnode); 302 + err_node: 303 + platform_driver_unregister(&surface_gpe_driver); 304 + return status; 305 + } 306 + module_init(surface_gpe_init); 307 + 308 + static void __exit surface_gpe_exit(void) 309 + { 310 + struct fwnode_handle *fwnode = surface_gpe_device->dev.fwnode; 311 + 312 + platform_device_unregister(surface_gpe_device); 313 + platform_driver_unregister(&surface_gpe_driver); 314 + fwnode_remove_software_node(fwnode); 315 + } 316 + module_exit(surface_gpe_exit); 317 + 318 + MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 319 + MODULE_DESCRIPTION("Surface GPE/Lid Driver"); 320 + MODULE_LICENSE("GPL"); 321 + MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurface*:*");
+60 -31
drivers/platform/x86/Kconfig
··· 191 191 If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M 192 192 here. 193 193 194 + config AMD_PMC 195 + tristate "AMD SoC PMC driver" 196 + depends on ACPI && PCI 197 + help 198 + The driver provides support for AMD Power Management Controller 199 + primarily responsible for S2Idle transactions that are driven from 200 + a platform firmware running on SMU. This driver also provides a debug 201 + mechanism to investigate the S2Idle transactions and failures. 202 + 203 + Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. 204 + 205 + If you choose to compile this driver as a module the module will be 206 + called amd-pmc. 207 + 194 208 config APPLE_GMUX 195 209 tristate "Apple Gmux Driver" 196 210 depends on ACPI && PCI ··· 454 440 455 441 To compile this driver as a module, choose M here: the module will 456 442 be called dell-wmi. 443 + 444 + config DELL_WMI_SYSMAN 445 + tristate "Dell WMI-based Systems management driver" 446 + depends on ACPI_WMI 447 + depends on DMI 448 + select NLS 449 + help 450 + This driver allows changing BIOS settings on many Dell machines from 451 + 2018 and newer without the use of any additional software. 452 + 453 + To compile this driver as a module, choose M here: the module will 454 + be called dell-wmi-sysman. 457 455 458 456 config DELL_WMI_DESCRIPTOR 459 457 tristate ··· 906 880 907 881 To compile this driver as a module, choose M here: the module will 908 882 be called intel_vbtn. 909 - 910 - config SURFACE3_WMI 911 - tristate "Surface 3 WMI Driver" 912 - depends on ACPI_WMI 913 - depends on DMI 914 - depends on INPUT 915 - depends on SPI 916 - help 917 - Say Y here if you have a Surface 3. 918 - 919 - To compile this driver as a module, choose M here: the module will 920 - be called surface3-wmi. 921 - 922 - config SURFACE_3_BUTTON 923 - tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet" 924 - depends on ACPI && KEYBOARD_GPIO && I2C 925 - help 926 - This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. 927 - 928 - config SURFACE_3_POWER_OPREGION 929 - tristate "Surface 3 battery platform operation region support" 930 - depends on ACPI && I2C 931 - help 932 - This driver provides support for ACPI operation 933 - region of the Surface 3 battery platform driver. 934 - 935 - config SURFACE_PRO3_BUTTON 936 - tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet" 937 - depends on ACPI && INPUT 938 - help 939 - This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. 940 883 941 884 config MSI_LAPTOP 942 885 tristate "MSI Laptop Extras" ··· 1367 1372 - PCH IP Power Gating status 1368 1373 - LTR Ignore 1369 1374 - MPHY/PLL gating status (Sunrisepoint PCH only) 1375 + 1376 + config INTEL_PMT_CLASS 1377 + tristate "Intel Platform Monitoring Technology (PMT) Class driver" 1378 + help 1379 + The Intel Platform Monitoring Technology (PMT) class driver provides 1380 + the basic sysfs interface and file hierarchy uses by PMT devices. 1381 + 1382 + For more information, see: 1383 + <file:Documentation/ABI/testing/sysfs-class-intel_pmt> 1384 + 1385 + To compile this driver as a module, choose M here: the module 1386 + will be called intel_pmt_class. 1387 + 1388 + config INTEL_PMT_TELEMETRY 1389 + tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" 1390 + select INTEL_PMT_CLASS 1391 + help 1392 + The Intel Platform Monitory Technology (PMT) Telemetry driver provides 1393 + access to hardware telemetry metrics on devices that support the 1394 + feature. 1395 + 1396 + To compile this driver as a module, choose M here: the module 1397 + will be called intel_pmt_telemetry. 1398 + 1399 + config INTEL_PMT_CRASHLOG 1400 + tristate "Intel Platform Monitoring Technology (PMT) Crashlog driver" 1401 + select INTEL_PMT_CLASS 1402 + help 1403 + The Intel Platform Monitoring Technology (PMT) crashlog driver provides 1404 + access to hardware crashlog capabilities on devices that support the 1405 + feature. 1406 + 1407 + To compile this driver as a module, choose M here: the module 1408 + will be called intel_pmt_crashlog. 1370 1409 1371 1410 config INTEL_PUNIT_IPC 1372 1411 tristate "Intel P-Unit IPC Driver"
+7 -6
drivers/platform/x86/Makefile
··· 22 22 obj-$(CONFIG_ACER_WIRELESS) += acer-wireless.o 23 23 obj-$(CONFIG_ACER_WMI) += acer-wmi.o 24 24 25 + # AMD 26 + obj-$(CONFIG_AMD_PMC) += amd-pmc.o 27 + 25 28 # Apple 26 29 obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o 27 30 ··· 50 47 obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o 51 48 obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o 52 49 obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o 50 + obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/ 53 51 54 52 # Fujitsu 55 53 obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o ··· 87 83 obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o 88 84 obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o 89 85 obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o 90 - 91 - # Microsoft 92 - obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o 93 - obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o 94 - obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o 95 - obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o 96 86 97 87 # MSI 98 88 obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o ··· 141 143 obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o 142 144 obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o 143 145 obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o 146 + obj-$(CONFIG_INTEL_PMT_CLASS) += intel_pmt_class.o 147 + obj-$(CONFIG_INTEL_PMT_TELEMETRY) += intel_pmt_telemetry.o 148 + obj-$(CONFIG_INTEL_PMT_CRASHLOG) += intel_pmt_crashlog.o 144 149 obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o 145 150 obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o 146 151 obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o
+1
drivers/platform/x86/acer-wireless.c
··· 28 28 return; 29 29 } 30 30 input_report_key(idev, KEY_RFKILL, 1); 31 + input_sync(idev); 31 32 input_report_key(idev, KEY_RFKILL, 0); 32 33 input_sync(idev); 33 34 }
+140 -35
drivers/platform/x86/acer-wmi.c
··· 30 30 #include <linux/input/sparse-keymap.h> 31 31 #include <acpi/video.h> 32 32 33 + ACPI_MODULE_NAME(KBUILD_MODNAME); 33 34 MODULE_AUTHOR("Carlos Corbacho"); 34 35 MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); 35 36 MODULE_LICENSE("GPL"); ··· 81 80 82 81 enum acer_wmi_event_ids { 83 82 WMID_HOTKEY_EVENT = 0x1, 84 - WMID_ACCEL_EVENT = 0x5, 83 + WMID_ACCEL_OR_KBD_DOCK_EVENT = 0x5, 85 84 }; 86 85 87 86 static const struct key_entry acer_wmi_keymap[] __initconst = { ··· 129 128 u8 function; 130 129 u8 key_num; 131 130 u16 device_state; 132 - u32 reserved; 131 + u16 reserved1; 132 + u8 kbd_dock_state; 133 + u8 reserved2; 133 134 } __attribute__((packed)); 134 135 135 136 /* ··· 209 206 /* 210 207 * Interface capability flags 211 208 */ 212 - #define ACER_CAP_MAILLED (1<<0) 213 - #define ACER_CAP_WIRELESS (1<<1) 214 - #define ACER_CAP_BLUETOOTH (1<<2) 215 - #define ACER_CAP_BRIGHTNESS (1<<3) 216 - #define ACER_CAP_THREEG (1<<4) 217 - #define ACER_CAP_ACCEL (1<<5) 218 - #define ACER_CAP_RFBTN (1<<6) 219 - #define ACER_CAP_ANY (0xFFFFFFFF) 209 + #define ACER_CAP_MAILLED BIT(0) 210 + #define ACER_CAP_WIRELESS BIT(1) 211 + #define ACER_CAP_BLUETOOTH BIT(2) 212 + #define ACER_CAP_BRIGHTNESS BIT(3) 213 + #define ACER_CAP_THREEG BIT(4) 214 + #define ACER_CAP_SET_FUNCTION_MODE BIT(5) 215 + #define ACER_CAP_KBD_DOCK BIT(6) 220 216 221 217 /* 222 218 * Interface type flags ··· 238 236 static int brightness = -1; 239 237 static int threeg = -1; 240 238 static int force_series; 239 + static int force_caps = -1; 241 240 static bool ec_raw_mode; 242 241 static bool has_type_aa; 243 242 static u16 commun_func_bitmap; ··· 248 245 module_param(brightness, int, 0444); 249 246 module_param(threeg, int, 0444); 250 247 module_param(force_series, int, 0444); 248 + module_param(force_caps, int, 0444); 251 249 module_param(ec_raw_mode, bool, 0444); 252 250 MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); 253 251 MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); 254 252 MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); 255 253 MODULE_PARM_DESC(force_series, "Force a different laptop series"); 254 + MODULE_PARM_DESC(force_caps, "Force the capability bitmask to this value"); 256 255 MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); 257 256 258 257 struct acer_data { ··· 308 303 309 304 static void __init set_quirks(void) 310 305 { 311 - if (!interface) 312 - return; 313 - 314 306 if (quirks->mailled) 315 307 interface->capability |= ACER_CAP_MAILLED; 316 308 ··· 318 316 static int __init dmi_matched(const struct dmi_system_id *dmi) 319 317 { 320 318 quirks = dmi->driver_data; 319 + return 1; 320 + } 321 + 322 + static int __init set_force_caps(const struct dmi_system_id *dmi) 323 + { 324 + if (force_caps == -1) { 325 + force_caps = (uintptr_t)dmi->driver_data; 326 + pr_info("Found %s, set force_caps to 0x%x\n", dmi->ident, force_caps); 327 + } 321 328 return 1; 322 329 } 323 330 ··· 508 497 }, 509 498 .driver_data = &quirk_acer_travelmate_2490, 510 499 }, 500 + { 501 + .callback = set_force_caps, 502 + .ident = "Acer Aspire Switch 10E SW3-016", 503 + .matches = { 504 + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 505 + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW3-016"), 506 + }, 507 + .driver_data = (void *)ACER_CAP_KBD_DOCK, 508 + }, 509 + { 510 + .callback = set_force_caps, 511 + .ident = "Acer Aspire Switch 10 SW5-012", 512 + .matches = { 513 + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 514 + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), 515 + }, 516 + .driver_data = (void *)ACER_CAP_KBD_DOCK, 517 + }, 518 + { 519 + .callback = set_force_caps, 520 + .ident = "Acer One 10 (S1003)", 521 + .matches = { 522 + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), 523 + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"), 524 + }, 525 + .driver_data = (void *)ACER_CAP_KBD_DOCK, 526 + }, 511 527 {} 512 528 }; 513 529 ··· 687 649 688 650 if (quirks == NULL) 689 651 quirks = &quirk_unknown; 690 - 691 - set_quirks(); 692 652 } 693 653 694 654 /* ··· 829 793 switch (quirks->brightness) { 830 794 default: 831 795 return ec_write(0x83, value); 832 - break; 833 796 } 834 797 default: 835 798 return AE_ERROR; ··· 1288 1253 interface->capability |= ACER_CAP_THREEG; 1289 1254 if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) 1290 1255 interface->capability |= ACER_CAP_BLUETOOTH; 1291 - if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_RFBTN) { 1292 - interface->capability |= ACER_CAP_RFBTN; 1256 + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_RFBTN) 1293 1257 commun_func_bitmap &= ~ACER_WMID3_GDS_RFBTN; 1294 - } 1295 1258 1296 1259 commun_fn_key_number = type_aa->commun_fn_key_number; 1297 1260 } ··· 1553 1520 struct acpi_buffer output; 1554 1521 union acpi_object out_obj[5]; 1555 1522 1556 - if (!has_cap(ACER_CAP_ACCEL)) 1523 + if (!acer_wmi_accel_dev) 1557 1524 return -1; 1558 1525 1559 1526 output.length = sizeof(out_obj); ··· 1574 1541 (s16)out_obj->package.elements[2].integer.value); 1575 1542 input_sync(acer_wmi_accel_dev); 1576 1543 return 0; 1544 + } 1545 + 1546 + /* 1547 + * Switch series keyboard dock status 1548 + */ 1549 + static int acer_kbd_dock_state_to_sw_tablet_mode(u8 kbd_dock_state) 1550 + { 1551 + switch (kbd_dock_state) { 1552 + case 0x01: /* Docked, traditional clamshell laptop mode */ 1553 + return 0; 1554 + case 0x04: /* Stand-alone tablet */ 1555 + case 0x40: /* Docked, tent mode, keyboard not usable */ 1556 + return 1; 1557 + default: 1558 + pr_warn("Unknown kbd_dock_state 0x%02x\n", kbd_dock_state); 1559 + } 1560 + 1561 + return 0; 1562 + } 1563 + 1564 + static void acer_kbd_dock_get_initial_state(void) 1565 + { 1566 + u8 *output, input[8] = { 0x05, 0x00, }; 1567 + struct acpi_buffer input_buf = { sizeof(input), input }; 1568 + struct acpi_buffer output_buf = { ACPI_ALLOCATE_BUFFER, NULL }; 1569 + union acpi_object *obj; 1570 + acpi_status status; 1571 + int sw_tablet_mode; 1572 + 1573 + status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input_buf, &output_buf); 1574 + if (ACPI_FAILURE(status)) { 1575 + ACPI_EXCEPTION((AE_INFO, status, "Error getting keyboard-dock initial status")); 1576 + return; 1577 + } 1578 + 1579 + obj = output_buf.pointer; 1580 + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) { 1581 + pr_err("Unexpected output format getting keyboard-dock initial status\n"); 1582 + goto out_free_obj; 1583 + } 1584 + 1585 + output = obj->buffer.pointer; 1586 + if (output[0] != 0x00 || (output[3] != 0x05 && output[3] != 0x45)) { 1587 + pr_err("Unexpected output [0]=0x%02x [3]=0x%02x getting keyboard-dock initial status\n", 1588 + output[0], output[3]); 1589 + goto out_free_obj; 1590 + } 1591 + 1592 + sw_tablet_mode = acer_kbd_dock_state_to_sw_tablet_mode(output[4]); 1593 + input_report_switch(acer_wmi_input_dev, SW_TABLET_MODE, sw_tablet_mode); 1594 + 1595 + out_free_obj: 1596 + kfree(obj); 1597 + } 1598 + 1599 + static void acer_kbd_dock_event(const struct event_return_value *event) 1600 + { 1601 + int sw_tablet_mode; 1602 + 1603 + if (!has_cap(ACER_CAP_KBD_DOCK)) 1604 + return; 1605 + 1606 + sw_tablet_mode = acer_kbd_dock_state_to_sw_tablet_mode(event->kbd_dock_state); 1607 + input_report_switch(acer_wmi_input_dev, SW_TABLET_MODE, sw_tablet_mode); 1608 + input_sync(acer_wmi_input_dev); 1577 1609 } 1578 1610 1579 1611 /* ··· 1868 1770 sparse_keymap_report_event(acer_wmi_input_dev, scancode, 1, true); 1869 1771 } 1870 1772 break; 1871 - case WMID_ACCEL_EVENT: 1773 + case WMID_ACCEL_OR_KBD_DOCK_EVENT: 1872 1774 acer_gsensor_event(); 1775 + acer_kbd_dock_event(&return_value); 1873 1776 break; 1874 1777 default: 1875 1778 pr_warn("Unknown function number - %d - %d\n", ··· 1993 1894 gsensor_handle = acpi_device_handle(adev); 1994 1895 acpi_dev_put(adev); 1995 1896 1996 - interface->capability |= ACER_CAP_ACCEL; 1997 - 1998 1897 acer_wmi_accel_dev = input_allocate_device(); 1999 1898 if (!acer_wmi_accel_dev) 2000 1899 return -ENOMEM; ··· 2018 1921 return err; 2019 1922 } 2020 1923 2021 - static void acer_wmi_accel_destroy(void) 2022 - { 2023 - input_unregister_device(acer_wmi_accel_dev); 2024 - } 2025 - 2026 1924 static int __init acer_wmi_input_setup(void) 2027 1925 { 2028 1926 acpi_status status; ··· 2035 1943 if (err) 2036 1944 goto err_free_dev; 2037 1945 1946 + if (has_cap(ACER_CAP_KBD_DOCK)) 1947 + input_set_capability(acer_wmi_input_dev, EV_SW, SW_TABLET_MODE); 1948 + 2038 1949 status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, 2039 1950 acer_wmi_notify, NULL); 2040 1951 if (ACPI_FAILURE(status)) { 2041 1952 err = -EIO; 2042 1953 goto err_free_dev; 2043 1954 } 1955 + 1956 + if (has_cap(ACER_CAP_KBD_DOCK)) 1957 + acer_kbd_dock_get_initial_state(); 2044 1958 2045 1959 err = input_register_device(acer_wmi_input_dev); 2046 1960 if (err) ··· 2178 2080 if (has_cap(ACER_CAP_BRIGHTNESS)) 2179 2081 set_u32(data->brightness, ACER_CAP_BRIGHTNESS); 2180 2082 2181 - if (has_cap(ACER_CAP_ACCEL)) 2083 + if (acer_wmi_accel_dev) 2182 2084 acer_gsensor_init(); 2183 2085 2184 2086 return 0; ··· 2279 2181 } 2280 2182 /* WMID always provides brightness methods */ 2281 2183 interface->capability |= ACER_CAP_BRIGHTNESS; 2282 - } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) { 2184 + } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa && force_caps == -1) { 2283 2185 pr_err("No WMID device detection method found\n"); 2284 2186 return -ENODEV; 2285 2187 } ··· 2309 2211 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 2310 2212 interface->capability &= ~ACER_CAP_BRIGHTNESS; 2311 2213 2312 - if (wmi_has_guid(WMID_GUID3)) { 2214 + if (wmi_has_guid(WMID_GUID3)) 2215 + interface->capability |= ACER_CAP_SET_FUNCTION_MODE; 2216 + 2217 + if (force_caps != -1) 2218 + interface->capability = force_caps; 2219 + 2220 + if (wmi_has_guid(WMID_GUID3) && 2221 + (interface->capability & ACER_CAP_SET_FUNCTION_MODE)) { 2313 2222 if (ACPI_FAILURE(acer_wmi_enable_rf_button())) 2314 2223 pr_warn("Cannot enable RF Button Driver\n"); 2315 2224 ··· 2375 2270 error_platform_register: 2376 2271 if (wmi_has_guid(ACERWMID_EVENT_GUID)) 2377 2272 acer_wmi_input_destroy(); 2378 - if (has_cap(ACER_CAP_ACCEL)) 2379 - acer_wmi_accel_destroy(); 2273 + if (acer_wmi_accel_dev) 2274 + input_unregister_device(acer_wmi_accel_dev); 2380 2275 2381 2276 return err; 2382 2277 } ··· 2386 2281 if (wmi_has_guid(ACERWMID_EVENT_GUID)) 2387 2282 acer_wmi_input_destroy(); 2388 2283 2389 - if (has_cap(ACER_CAP_ACCEL)) 2390 - acer_wmi_accel_destroy(); 2284 + if (acer_wmi_accel_dev) 2285 + input_unregister_device(acer_wmi_accel_dev); 2391 2286 2392 2287 remove_debugfs(); 2393 2288 platform_device_unregister(acer_platform_device);
+286
drivers/platform/x86/amd-pmc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * AMD SoC Power Management Controller Driver 4 + * 5 + * Copyright (c) 2020, Advanced Micro Devices, Inc. 6 + * All Rights Reserved. 7 + * 8 + * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 9 + */ 10 + 11 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 + 13 + #include <linux/acpi.h> 14 + #include <linux/bitfield.h> 15 + #include <linux/bits.h> 16 + #include <linux/debugfs.h> 17 + #include <linux/delay.h> 18 + #include <linux/io.h> 19 + #include <linux/iopoll.h> 20 + #include <linux/module.h> 21 + #include <linux/pci.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/suspend.h> 24 + #include <linux/seq_file.h> 25 + #include <linux/uaccess.h> 26 + 27 + /* SMU communication registers */ 28 + #define AMD_PMC_REGISTER_MESSAGE 0x538 29 + #define AMD_PMC_REGISTER_RESPONSE 0x980 30 + #define AMD_PMC_REGISTER_ARGUMENT 0x9BC 31 + 32 + /* Base address of SMU for mapping physical address to virtual address */ 33 + #define AMD_PMC_SMU_INDEX_ADDRESS 0xB8 34 + #define AMD_PMC_SMU_INDEX_DATA 0xBC 35 + #define AMD_PMC_MAPPING_SIZE 0x01000 36 + #define AMD_PMC_BASE_ADDR_OFFSET 0x10000 37 + #define AMD_PMC_BASE_ADDR_LO 0x13B102E8 38 + #define AMD_PMC_BASE_ADDR_HI 0x13B102EC 39 + #define AMD_PMC_BASE_ADDR_LO_MASK GENMASK(15, 0) 40 + #define AMD_PMC_BASE_ADDR_HI_MASK GENMASK(31, 20) 41 + 42 + /* SMU Response Codes */ 43 + #define AMD_PMC_RESULT_OK 0x01 44 + #define AMD_PMC_RESULT_CMD_REJECT_BUSY 0xFC 45 + #define AMD_PMC_RESULT_CMD_REJECT_PREREQ 0xFD 46 + #define AMD_PMC_RESULT_CMD_UNKNOWN 0xFE 47 + #define AMD_PMC_RESULT_FAILED 0xFF 48 + 49 + /* List of supported CPU ids */ 50 + #define AMD_CPU_ID_RV 0x15D0 51 + #define AMD_CPU_ID_RN 0x1630 52 + #define AMD_CPU_ID_PCO AMD_CPU_ID_RV 53 + #define AMD_CPU_ID_CZN AMD_CPU_ID_RN 54 + 55 + #define AMD_SMU_FW_VERSION 0x0 56 + #define PMC_MSG_DELAY_MIN_US 100 57 + #define RESPONSE_REGISTER_LOOP_MAX 200 58 + 59 + enum amd_pmc_def { 60 + MSG_TEST = 0x01, 61 + MSG_OS_HINT_PCO, 62 + MSG_OS_HINT_RN, 63 + }; 64 + 65 + struct amd_pmc_dev { 66 + void __iomem *regbase; 67 + void __iomem *smu_base; 68 + u32 base_addr; 69 + u32 cpu_id; 70 + struct device *dev; 71 + #if IS_ENABLED(CONFIG_DEBUG_FS) 72 + struct dentry *dbgfs_dir; 73 + #endif /* CONFIG_DEBUG_FS */ 74 + }; 75 + 76 + static struct amd_pmc_dev pmc; 77 + 78 + static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) 79 + { 80 + return ioread32(dev->regbase + reg_offset); 81 + } 82 + 83 + static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u32 val) 84 + { 85 + iowrite32(val, dev->regbase + reg_offset); 86 + } 87 + 88 + #if CONFIG_DEBUG_FS 89 + static int smu_fw_info_show(struct seq_file *s, void *unused) 90 + { 91 + struct amd_pmc_dev *dev = s->private; 92 + u32 value; 93 + 94 + value = ioread32(dev->smu_base + AMD_SMU_FW_VERSION); 95 + seq_printf(s, "SMU FW Info: %x\n", value); 96 + return 0; 97 + } 98 + DEFINE_SHOW_ATTRIBUTE(smu_fw_info); 99 + 100 + static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) 101 + { 102 + debugfs_remove_recursive(dev->dbgfs_dir); 103 + } 104 + 105 + static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) 106 + { 107 + dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL); 108 + debugfs_create_file("smu_fw_info", 0644, dev->dbgfs_dir, dev, 109 + &smu_fw_info_fops); 110 + } 111 + #else 112 + static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) 113 + { 114 + } 115 + 116 + static inline void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) 117 + { 118 + } 119 + #endif /* CONFIG_DEBUG_FS */ 120 + 121 + static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) 122 + { 123 + u32 value; 124 + 125 + value = amd_pmc_reg_read(dev, AMD_PMC_REGISTER_RESPONSE); 126 + dev_dbg(dev->dev, "AMD_PMC_REGISTER_RESPONSE:%x\n", value); 127 + 128 + value = amd_pmc_reg_read(dev, AMD_PMC_REGISTER_ARGUMENT); 129 + dev_dbg(dev->dev, "AMD_PMC_REGISTER_ARGUMENT:%x\n", value); 130 + 131 + value = amd_pmc_reg_read(dev, AMD_PMC_REGISTER_MESSAGE); 132 + dev_dbg(dev->dev, "AMD_PMC_REGISTER_MESSAGE:%x\n", value); 133 + } 134 + 135 + static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set) 136 + { 137 + int rc; 138 + u8 msg; 139 + u32 val; 140 + 141 + /* Wait until we get a valid response */ 142 + rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMC_REGISTER_RESPONSE, 143 + val, val > 0, PMC_MSG_DELAY_MIN_US, 144 + PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX); 145 + if (rc) { 146 + dev_err(dev->dev, "failed to talk to SMU\n"); 147 + return rc; 148 + } 149 + 150 + /* Write zero to response register */ 151 + amd_pmc_reg_write(dev, AMD_PMC_REGISTER_RESPONSE, 0); 152 + 153 + /* Write argument into response register */ 154 + amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, set); 155 + 156 + /* Write message ID to message ID register */ 157 + msg = (dev->cpu_id == AMD_CPU_ID_RN) ? MSG_OS_HINT_RN : MSG_OS_HINT_PCO; 158 + amd_pmc_reg_write(dev, AMD_PMC_REGISTER_MESSAGE, msg); 159 + return 0; 160 + } 161 + 162 + static int __maybe_unused amd_pmc_suspend(struct device *dev) 163 + { 164 + struct amd_pmc_dev *pdev = dev_get_drvdata(dev); 165 + int rc; 166 + 167 + rc = amd_pmc_send_cmd(pdev, 1); 168 + if (rc) 169 + dev_err(pdev->dev, "suspend failed\n"); 170 + 171 + amd_pmc_dump_registers(pdev); 172 + return 0; 173 + } 174 + 175 + static int __maybe_unused amd_pmc_resume(struct device *dev) 176 + { 177 + struct amd_pmc_dev *pdev = dev_get_drvdata(dev); 178 + int rc; 179 + 180 + rc = amd_pmc_send_cmd(pdev, 0); 181 + if (rc) 182 + dev_err(pdev->dev, "resume failed\n"); 183 + 184 + amd_pmc_dump_registers(pdev); 185 + return 0; 186 + } 187 + 188 + static const struct dev_pm_ops amd_pmc_pm_ops = { 189 + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(amd_pmc_suspend, amd_pmc_resume) 190 + }; 191 + 192 + static const struct pci_device_id pmc_pci_ids[] = { 193 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) }, 194 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, 195 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, 196 + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, 197 + { } 198 + }; 199 + 200 + static int amd_pmc_probe(struct platform_device *pdev) 201 + { 202 + struct amd_pmc_dev *dev = &pmc; 203 + struct pci_dev *rdev; 204 + u32 base_addr_lo; 205 + u32 base_addr_hi; 206 + u64 base_addr; 207 + int err; 208 + u32 val; 209 + 210 + dev->dev = &pdev->dev; 211 + 212 + rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); 213 + if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) 214 + return -ENODEV; 215 + 216 + dev->cpu_id = rdev->device; 217 + err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO); 218 + if (err) { 219 + dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); 220 + return pcibios_err_to_errno(err); 221 + } 222 + 223 + err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); 224 + if (err) 225 + return pcibios_err_to_errno(err); 226 + 227 + base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; 228 + 229 + err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI); 230 + if (err) { 231 + dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); 232 + return pcibios_err_to_errno(err); 233 + } 234 + 235 + err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); 236 + if (err) 237 + return pcibios_err_to_errno(err); 238 + 239 + base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK; 240 + pci_dev_put(rdev); 241 + base_addr = ((u64)base_addr_hi << 32 | base_addr_lo); 242 + 243 + dev->smu_base = devm_ioremap(dev->dev, base_addr, AMD_PMC_MAPPING_SIZE); 244 + if (!dev->smu_base) 245 + return -ENOMEM; 246 + 247 + dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET, 248 + AMD_PMC_MAPPING_SIZE); 249 + if (!dev->regbase) 250 + return -ENOMEM; 251 + 252 + amd_pmc_dump_registers(dev); 253 + 254 + platform_set_drvdata(pdev, dev); 255 + amd_pmc_dbgfs_register(dev); 256 + return 0; 257 + } 258 + 259 + static int amd_pmc_remove(struct platform_device *pdev) 260 + { 261 + struct amd_pmc_dev *dev = platform_get_drvdata(pdev); 262 + 263 + amd_pmc_dbgfs_unregister(dev); 264 + return 0; 265 + } 266 + 267 + static const struct acpi_device_id amd_pmc_acpi_ids[] = { 268 + {"AMDI0005", 0}, 269 + {"AMD0004", 0}, 270 + { } 271 + }; 272 + MODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids); 273 + 274 + static struct platform_driver amd_pmc_driver = { 275 + .driver = { 276 + .name = "amd_pmc", 277 + .acpi_match_table = amd_pmc_acpi_ids, 278 + .pm = &amd_pmc_pm_ops, 279 + }, 280 + .probe = amd_pmc_probe, 281 + .remove = amd_pmc_remove, 282 + }; 283 + module_platform_driver(amd_pmc_driver); 284 + 285 + MODULE_LICENSE("GPL v2"); 286 + MODULE_DESCRIPTION("AMD PMC Driver");
+15
drivers/platform/x86/asus-nb-wmi.c
··· 119 119 .use_kbd_dock_devid = true, 120 120 }; 121 121 122 + static struct quirk_entry quirk_asus_use_lid_flip_devid = { 123 + .wmi_backlight_set_devstate = true, 124 + .use_lid_flip_devid = true, 125 + }; 126 + 122 127 static int dmi_matched(const struct dmi_system_id *dmi) 123 128 { 124 129 pr_info("Identified laptop model '%s'\n", dmi->ident); ··· 524 519 DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), 525 520 }, 526 521 .driver_data = &quirk_asus_use_kbd_dock_devid, 522 + }, 523 + { 524 + .callback = dmi_matched, 525 + .ident = "ASUS ZenBook Flip UX360", 526 + .matches = { 527 + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 528 + /* Match UX360* */ 529 + DMI_MATCH(DMI_PRODUCT_NAME, "UX360"), 530 + }, 531 + .driver_data = &quirk_asus_use_lid_flip_devid, 527 532 }, 528 533 {}, 529 534 };
+48
drivers/platform/x86/asus-wmi.c
··· 63 63 #define NOTIFY_KBD_BRTTOGGLE 0xc7 64 64 #define NOTIFY_KBD_FBM 0x99 65 65 #define NOTIFY_KBD_TTP 0xae 66 + #define NOTIFY_LID_FLIP 0xfa 66 67 67 68 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) 68 69 ··· 376 375 } 377 376 } 378 377 378 + if (asus->driver->quirks->use_lid_flip_devid) { 379 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP); 380 + if (result < 0) 381 + asus->driver->quirks->use_lid_flip_devid = 0; 382 + if (result >= 0) { 383 + input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE); 384 + input_report_switch(asus->inputdev, SW_TABLET_MODE, result); 385 + } else if (result == -ENODEV) { 386 + pr_err("This device has lid_flip quirk but got ENODEV checking it. This is a bug."); 387 + } else { 388 + pr_err("Error checking for lid-flip: %d\n", result); 389 + } 390 + } 391 + 379 392 err = input_register_device(asus->inputdev); 380 393 if (err) 381 394 goto err_free_dev; ··· 407 392 input_unregister_device(asus->inputdev); 408 393 409 394 asus->inputdev = NULL; 395 + } 396 + 397 + /* Tablet mode ****************************************************************/ 398 + 399 + static void lid_flip_tablet_mode_get_state(struct asus_wmi *asus) 400 + { 401 + int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_LID_FLIP); 402 + 403 + if (result >= 0) { 404 + input_report_switch(asus->inputdev, SW_TABLET_MODE, result); 405 + input_sync(asus->inputdev); 406 + } 410 407 } 411 408 412 409 /* Battery ********************************************************************/ ··· 1690 1663 pr_info("Set fan boost mode: %u\n", value); 1691 1664 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value, 1692 1665 &retval); 1666 + 1667 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, 1668 + "fan_boost_mode"); 1669 + 1693 1670 if (err) { 1694 1671 pr_warn("Failed to set fan boost mode: %d\n", err); 1695 1672 return err; ··· 1805 1774 1806 1775 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, 1807 1776 value, &retval); 1777 + 1778 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, 1779 + "throttle_thermal_policy"); 1780 + 1808 1781 if (err) { 1809 1782 pr_warn("Failed to set throttle thermal policy: %d\n", err); 1810 1783 return err; ··· 2160 2125 !result); 2161 2126 input_sync(asus->inputdev); 2162 2127 } 2128 + return; 2129 + } 2130 + 2131 + if (asus->driver->quirks->use_lid_flip_devid && code == NOTIFY_LID_FLIP) { 2132 + lid_flip_tablet_mode_get_state(asus); 2163 2133 return; 2164 2134 } 2165 2135 ··· 2759 2719 2760 2720 if (asus_wmi_has_fnlock_key(asus)) 2761 2721 asus_wmi_fnlock_update(asus); 2722 + 2723 + if (asus->driver->quirks->use_lid_flip_devid) 2724 + lid_flip_tablet_mode_get_state(asus); 2725 + 2762 2726 return 0; 2763 2727 } 2764 2728 ··· 2801 2757 2802 2758 if (asus_wmi_has_fnlock_key(asus)) 2803 2759 asus_wmi_fnlock_update(asus); 2760 + 2761 + if (asus->driver->quirks->use_lid_flip_devid) 2762 + lid_flip_tablet_mode_get_state(asus); 2763 + 2804 2764 return 0; 2805 2765 } 2806 2766
+1
drivers/platform/x86/asus-wmi.h
··· 34 34 bool wmi_backlight_set_devstate; 35 35 bool wmi_force_als_set; 36 36 bool use_kbd_dock_devid; 37 + bool use_lid_flip_devid; 37 38 int wapf; 38 39 /* 39 40 * For machines with AMD graphic chips, it will send out WMI event
+2
drivers/platform/x86/classmate-laptop.c
··· 1023 1023 KEY_CAMERA, 1024 1024 KEY_BACK, 1025 1025 KEY_FORWARD, 1026 + KEY_UNKNOWN, 1027 + KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */ 1026 1028 KEY_MAX 1027 1029 }; 1028 1030
+1
drivers/platform/x86/dell-smbios-base.c
··· 594 594 if (wmi && smm) { 595 595 pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n", 596 596 wmi, smm); 597 + ret = -ENODEV; 597 598 goto fail_create_group; 598 599 } 599 600
+8
drivers/platform/x86/dell-wmi-sysman/Makefile
··· 1 + obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o 2 + dell-wmi-sysman-objs := sysman.o \ 3 + enum-attributes.o \ 4 + int-attributes.o \ 5 + string-attributes.o \ 6 + passobj-attributes.o \ 7 + biosattr-interface.o \ 8 + passwordattr-interface.o
+186
drivers/platform/x86/dell-wmi-sysman/biosattr-interface.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to SET methods under BIOS attributes interface GUID for use 4 + * with dell-wmi-sysman 5 + * 6 + * Copyright (c) 2020 Dell Inc. 7 + */ 8 + 9 + #include <linux/wmi.h> 10 + #include "dell-wmi-sysman.h" 11 + 12 + #define SETDEFAULTVALUES_METHOD_ID 0x02 13 + #define SETBIOSDEFAULTS_METHOD_ID 0x03 14 + #define SETATTRIBUTE_METHOD_ID 0x04 15 + 16 + static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, 17 + int method_id) 18 + { 19 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 20 + struct acpi_buffer input; 21 + union acpi_object *obj; 22 + acpi_status status; 23 + int ret = -EIO; 24 + 25 + input.length = (acpi_size) size; 26 + input.pointer = in_args; 27 + status = wmidev_evaluate_method(wdev, 0, method_id, &input, &output); 28 + if (ACPI_FAILURE(status)) 29 + return -EIO; 30 + obj = (union acpi_object *)output.pointer; 31 + if (obj->type == ACPI_TYPE_INTEGER) 32 + ret = obj->integer.value; 33 + 34 + if (wmi_priv.pending_changes == 0) { 35 + wmi_priv.pending_changes = 1; 36 + /* let userland know it may need to check reboot pending again */ 37 + kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE); 38 + } 39 + kfree(output.pointer); 40 + return map_wmi_error(ret); 41 + } 42 + 43 + /** 44 + * set_attribute() - Update an attribute value 45 + * @a_name: The attribute name 46 + * @a_value: The attribute value 47 + * 48 + * Sets an attribute to new value 49 + */ 50 + int set_attribute(const char *a_name, const char *a_value) 51 + { 52 + size_t security_area_size, buffer_size; 53 + size_t a_name_size, a_value_size; 54 + char *buffer = NULL, *start; 55 + int ret; 56 + 57 + mutex_lock(&wmi_priv.mutex); 58 + if (!wmi_priv.bios_attr_wdev) { 59 + ret = -ENODEV; 60 + goto out; 61 + } 62 + 63 + /* build/calculate buffer */ 64 + security_area_size = calculate_security_buffer(wmi_priv.current_admin_password); 65 + a_name_size = calculate_string_buffer(a_name); 66 + a_value_size = calculate_string_buffer(a_value); 67 + buffer_size = security_area_size + a_name_size + a_value_size; 68 + buffer = kzalloc(buffer_size, GFP_KERNEL); 69 + if (!buffer) { 70 + ret = -ENOMEM; 71 + goto out; 72 + } 73 + 74 + /* build security area */ 75 + populate_security_buffer(buffer, wmi_priv.current_admin_password); 76 + 77 + /* build variables to set */ 78 + start = buffer + security_area_size; 79 + ret = populate_string_buffer(start, a_name_size, a_name); 80 + if (ret < 0) 81 + goto out; 82 + start += ret; 83 + ret = populate_string_buffer(start, a_value_size, a_value); 84 + if (ret < 0) 85 + goto out; 86 + 87 + print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size); 88 + ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, 89 + buffer, buffer_size, 90 + SETATTRIBUTE_METHOD_ID); 91 + if (ret == -EOPNOTSUPP) 92 + dev_err(&wmi_priv.bios_attr_wdev->dev, "admin password must be configured\n"); 93 + else if (ret == -EACCES) 94 + dev_err(&wmi_priv.bios_attr_wdev->dev, "invalid password\n"); 95 + 96 + out: 97 + kfree(buffer); 98 + mutex_unlock(&wmi_priv.mutex); 99 + return ret; 100 + } 101 + 102 + /** 103 + * set_bios_defaults() - Resets BIOS defaults 104 + * @deftype: the type of BIOS value reset to issue. 105 + * 106 + * Resets BIOS defaults 107 + */ 108 + int set_bios_defaults(u8 deftype) 109 + { 110 + size_t security_area_size, buffer_size; 111 + size_t integer_area_size = sizeof(u8); 112 + char *buffer = NULL; 113 + u8 *defaultType; 114 + int ret; 115 + 116 + mutex_lock(&wmi_priv.mutex); 117 + if (!wmi_priv.bios_attr_wdev) { 118 + ret = -ENODEV; 119 + goto out; 120 + } 121 + 122 + security_area_size = calculate_security_buffer(wmi_priv.current_admin_password); 123 + buffer_size = security_area_size + integer_area_size; 124 + buffer = kzalloc(buffer_size, GFP_KERNEL); 125 + if (!buffer) { 126 + ret = -ENOMEM; 127 + goto out; 128 + } 129 + 130 + /* build security area */ 131 + populate_security_buffer(buffer, wmi_priv.current_admin_password); 132 + 133 + defaultType = buffer + security_area_size; 134 + *defaultType = deftype; 135 + 136 + ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, buffer, buffer_size, 137 + SETBIOSDEFAULTS_METHOD_ID); 138 + if (ret) 139 + dev_err(&wmi_priv.bios_attr_wdev->dev, "reset BIOS defaults failed: %d\n", ret); 140 + 141 + kfree(buffer); 142 + out: 143 + mutex_unlock(&wmi_priv.mutex); 144 + return ret; 145 + } 146 + 147 + static int bios_attr_set_interface_probe(struct wmi_device *wdev, const void *context) 148 + { 149 + mutex_lock(&wmi_priv.mutex); 150 + wmi_priv.bios_attr_wdev = wdev; 151 + mutex_unlock(&wmi_priv.mutex); 152 + return 0; 153 + } 154 + 155 + static int bios_attr_set_interface_remove(struct wmi_device *wdev) 156 + { 157 + mutex_lock(&wmi_priv.mutex); 158 + wmi_priv.bios_attr_wdev = NULL; 159 + mutex_unlock(&wmi_priv.mutex); 160 + return 0; 161 + } 162 + 163 + static const struct wmi_device_id bios_attr_set_interface_id_table[] = { 164 + { .guid_string = DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID }, 165 + { }, 166 + }; 167 + static struct wmi_driver bios_attr_set_interface_driver = { 168 + .driver = { 169 + .name = DRIVER_NAME 170 + }, 171 + .probe = bios_attr_set_interface_probe, 172 + .remove = bios_attr_set_interface_remove, 173 + .id_table = bios_attr_set_interface_id_table, 174 + }; 175 + 176 + int init_bios_attr_set_interface(void) 177 + { 178 + return wmi_driver_register(&bios_attr_set_interface_driver); 179 + } 180 + 181 + void exit_bios_attr_set_interface(void) 182 + { 183 + wmi_driver_unregister(&bios_attr_set_interface_driver); 184 + } 185 + 186 + MODULE_DEVICE_TABLE(wmi, bios_attr_set_interface_id_table);
+191
drivers/platform/x86/dell-wmi-sysman/dell-wmi-sysman.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 2 + * Definitions for kernel modules using Dell WMI System Management Driver 3 + * 4 + * Copyright (c) 2020 Dell Inc. 5 + */ 6 + 7 + #ifndef _DELL_WMI_BIOS_ATTR_H_ 8 + #define _DELL_WMI_BIOS_ATTR_H_ 9 + 10 + #include <linux/wmi.h> 11 + #include <linux/device.h> 12 + #include <linux/module.h> 13 + #include <linux/kernel.h> 14 + #include <linux/capability.h> 15 + 16 + #define DRIVER_NAME "dell-wmi-sysman" 17 + #define MAX_BUFF 512 18 + 19 + #define DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF5" 20 + #define DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BFA" 21 + #define DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF9" 22 + #define DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID "0894B8D6-44A6-4719-97D7-6AD24108BFD4" 23 + #define DELL_WMI_BIOS_ATTRIBUTES_INTERFACE_GUID "F1DDEE52-063C-4784-A11E-8A06684B9BF4" 24 + #define DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID "70FE8229-D03B-4214-A1C6-1F884B1A892A" 25 + 26 + struct enumeration_data { 27 + struct kobject *attr_name_kobj; 28 + char display_name_language_code[MAX_BUFF]; 29 + char dell_value_modifier[MAX_BUFF]; 30 + char possible_values[MAX_BUFF]; 31 + char attribute_name[MAX_BUFF]; 32 + char default_value[MAX_BUFF]; 33 + char dell_modifier[MAX_BUFF]; 34 + char display_name[MAX_BUFF]; 35 + }; 36 + 37 + struct integer_data { 38 + struct kobject *attr_name_kobj; 39 + char display_name_language_code[MAX_BUFF]; 40 + char attribute_name[MAX_BUFF]; 41 + char dell_modifier[MAX_BUFF]; 42 + char display_name[MAX_BUFF]; 43 + int scalar_increment; 44 + int default_value; 45 + int min_value; 46 + int max_value; 47 + }; 48 + 49 + struct str_data { 50 + struct kobject *attr_name_kobj; 51 + char display_name_language_code[MAX_BUFF]; 52 + char attribute_name[MAX_BUFF]; 53 + char display_name[MAX_BUFF]; 54 + char default_value[MAX_BUFF]; 55 + char dell_modifier[MAX_BUFF]; 56 + int min_length; 57 + int max_length; 58 + }; 59 + 60 + struct po_data { 61 + struct kobject *attr_name_kobj; 62 + char attribute_name[MAX_BUFF]; 63 + int min_password_length; 64 + int max_password_length; 65 + }; 66 + 67 + struct wmi_sysman_priv { 68 + char current_admin_password[MAX_BUFF]; 69 + char current_system_password[MAX_BUFF]; 70 + struct wmi_device *password_attr_wdev; 71 + struct wmi_device *bios_attr_wdev; 72 + struct kset *authentication_dir_kset; 73 + struct kset *main_dir_kset; 74 + struct device *class_dev; 75 + struct enumeration_data *enumeration_data; 76 + int enumeration_instances_count; 77 + struct integer_data *integer_data; 78 + int integer_instances_count; 79 + struct str_data *str_data; 80 + int str_instances_count; 81 + struct po_data *po_data; 82 + int po_instances_count; 83 + bool pending_changes; 84 + struct mutex mutex; 85 + }; 86 + 87 + /* global structure used by multiple WMI interfaces */ 88 + extern struct wmi_sysman_priv wmi_priv; 89 + 90 + enum { ENUM, INT, STR, PO }; 91 + 92 + enum { 93 + ATTR_NAME, 94 + DISPL_NAME_LANG_CODE, 95 + DISPLAY_NAME, 96 + DEFAULT_VAL, 97 + CURRENT_VAL, 98 + MODIFIER 99 + }; 100 + 101 + #define get_instance_id(type) \ 102 + static int get_##type##_instance_id(struct kobject *kobj) \ 103 + { \ 104 + int i; \ 105 + for (i = 0; i <= wmi_priv.type##_instances_count; i++) { \ 106 + if (!(strcmp(kobj->name, wmi_priv.type##_data[i].attribute_name)))\ 107 + return i; \ 108 + } \ 109 + return -EIO; \ 110 + } 111 + 112 + #define attribute_s_property_show(name, type) \ 113 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ 114 + char *buf) \ 115 + { \ 116 + int i = get_##type##_instance_id(kobj); \ 117 + if (i >= 0) \ 118 + return sprintf(buf, "%s\n", wmi_priv.type##_data[i].name); \ 119 + return 0; \ 120 + } 121 + 122 + #define attribute_n_property_show(name, type) \ 123 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ 124 + char *buf) \ 125 + { \ 126 + int i = get_##type##_instance_id(kobj); \ 127 + if (i >= 0) \ 128 + return sprintf(buf, "%d\n", wmi_priv.type##_data[i].name); \ 129 + return 0; \ 130 + } 131 + 132 + #define attribute_property_store(curr_val, type) \ 133 + static ssize_t curr_val##_store(struct kobject *kobj, \ 134 + struct kobj_attribute *attr, \ 135 + const char *buf, size_t count) \ 136 + { \ 137 + char *p, *buf_cp; \ 138 + int i, ret = -EIO; \ 139 + buf_cp = kstrdup(buf, GFP_KERNEL); \ 140 + if (!buf_cp) \ 141 + return -ENOMEM; \ 142 + p = memchr(buf_cp, '\n', count); \ 143 + \ 144 + if (p != NULL) \ 145 + *p = '\0'; \ 146 + i = get_##type##_instance_id(kobj); \ 147 + if (i >= 0) \ 148 + ret = validate_##type##_input(i, buf_cp); \ 149 + if (!ret) \ 150 + ret = set_attribute(kobj->name, buf_cp); \ 151 + kfree(buf_cp); \ 152 + return ret ? ret : count; \ 153 + } 154 + 155 + union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string); 156 + int get_instance_count(const char *guid_string); 157 + void strlcpy_attr(char *dest, char *src); 158 + 159 + int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, 160 + struct kobject *attr_name_kobj); 161 + int alloc_enum_data(void); 162 + void exit_enum_attributes(void); 163 + 164 + int populate_int_data(union acpi_object *integer_obj, int instance_id, 165 + struct kobject *attr_name_kobj); 166 + int alloc_int_data(void); 167 + void exit_int_attributes(void); 168 + 169 + int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj); 170 + int alloc_str_data(void); 171 + void exit_str_attributes(void); 172 + 173 + int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj); 174 + int alloc_po_data(void); 175 + void exit_po_attributes(void); 176 + 177 + int set_attribute(const char *a_name, const char *a_value); 178 + int set_bios_defaults(u8 defType); 179 + 180 + void exit_bios_attr_set_interface(void); 181 + int init_bios_attr_set_interface(void); 182 + int map_wmi_error(int error_code); 183 + size_t calculate_string_buffer(const char *str); 184 + size_t calculate_security_buffer(char *authentication); 185 + void populate_security_buffer(char *buffer, char *authentication); 186 + ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str); 187 + int set_new_password(const char *password_type, const char *new); 188 + int init_bios_attr_pass_interface(void); 189 + void exit_bios_attr_pass_interface(void); 190 + 191 + #endif
+189
drivers/platform/x86/dell-wmi-sysman/enum-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to enumeration type attributes under 4 + * BIOS Enumeration GUID for use with dell-wmi-sysman 5 + * 6 + * Copyright (c) 2020 Dell Inc. 7 + */ 8 + 9 + #include "dell-wmi-sysman.h" 10 + 11 + get_instance_id(enumeration); 12 + 13 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 14 + { 15 + int instance_id = get_enumeration_instance_id(kobj); 16 + union acpi_object *obj; 17 + ssize_t ret; 18 + 19 + if (instance_id < 0) 20 + return instance_id; 21 + 22 + /* need to use specific instance_id and guid combination to get right data */ 23 + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); 24 + if (!obj) 25 + return -EIO; 26 + if (obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_STRING) { 27 + kfree(obj); 28 + return -EINVAL; 29 + } 30 + ret = snprintf(buf, PAGE_SIZE, "%s\n", obj->package.elements[CURRENT_VAL].string.pointer); 31 + kfree(obj); 32 + return ret; 33 + } 34 + 35 + /** 36 + * validate_enumeration_input() - Validate input of current_value against possible values 37 + * @instance_id: The instance on which input is validated 38 + * @buf: Input value 39 + */ 40 + static int validate_enumeration_input(int instance_id, const char *buf) 41 + { 42 + char *options, *tmp, *p; 43 + int ret = -EINVAL; 44 + 45 + options = tmp = kstrdup(wmi_priv.enumeration_data[instance_id].possible_values, 46 + GFP_KERNEL); 47 + if (!options) 48 + return -ENOMEM; 49 + 50 + while ((p = strsep(&options, ";")) != NULL) { 51 + if (!*p) 52 + continue; 53 + if (!strcasecmp(p, buf)) { 54 + ret = 0; 55 + break; 56 + } 57 + } 58 + 59 + kfree(tmp); 60 + return ret; 61 + } 62 + 63 + attribute_s_property_show(display_name_language_code, enumeration); 64 + static struct kobj_attribute displ_langcode = 65 + __ATTR_RO(display_name_language_code); 66 + 67 + attribute_s_property_show(display_name, enumeration); 68 + static struct kobj_attribute displ_name = 69 + __ATTR_RO(display_name); 70 + 71 + attribute_s_property_show(default_value, enumeration); 72 + static struct kobj_attribute default_val = 73 + __ATTR_RO(default_value); 74 + 75 + attribute_property_store(current_value, enumeration); 76 + static struct kobj_attribute current_val = 77 + __ATTR_RW_MODE(current_value, 0600); 78 + 79 + attribute_s_property_show(dell_modifier, enumeration); 80 + static struct kobj_attribute modifier = 81 + __ATTR_RO(dell_modifier); 82 + 83 + attribute_s_property_show(dell_value_modifier, enumeration); 84 + static struct kobj_attribute value_modfr = 85 + __ATTR_RO(dell_value_modifier); 86 + 87 + attribute_s_property_show(possible_values, enumeration); 88 + static struct kobj_attribute poss_val = 89 + __ATTR_RO(possible_values); 90 + 91 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 92 + char *buf) 93 + { 94 + return sprintf(buf, "enumeration\n"); 95 + } 96 + static struct kobj_attribute type = 97 + __ATTR_RO(type); 98 + 99 + static struct attribute *enumeration_attrs[] = { 100 + &displ_langcode.attr, 101 + &displ_name.attr, 102 + &default_val.attr, 103 + &current_val.attr, 104 + &modifier.attr, 105 + &value_modfr.attr, 106 + &poss_val.attr, 107 + &type.attr, 108 + NULL, 109 + }; 110 + 111 + static const struct attribute_group enumeration_attr_group = { 112 + .attrs = enumeration_attrs, 113 + }; 114 + 115 + int alloc_enum_data(void) 116 + { 117 + int ret = 0; 118 + 119 + wmi_priv.enumeration_instances_count = 120 + get_instance_count(DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); 121 + wmi_priv.enumeration_data = kcalloc(wmi_priv.enumeration_instances_count, 122 + sizeof(struct enumeration_data), GFP_KERNEL); 123 + if (!wmi_priv.enumeration_data) { 124 + wmi_priv.enumeration_instances_count = 0; 125 + ret = -ENOMEM; 126 + } 127 + return ret; 128 + } 129 + 130 + /** 131 + * populate_enum_data() - Populate all properties of an instance under enumeration attribute 132 + * @enumeration_obj: ACPI object with enumeration data 133 + * @instance_id: The instance to enumerate 134 + * @attr_name_kobj: The parent kernel object 135 + */ 136 + int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, 137 + struct kobject *attr_name_kobj) 138 + { 139 + int i, next_obj, value_modifier_count, possible_values_count; 140 + 141 + wmi_priv.enumeration_data[instance_id].attr_name_kobj = attr_name_kobj; 142 + strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name, 143 + enumeration_obj[ATTR_NAME].string.pointer); 144 + strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code, 145 + enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer); 146 + strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name, 147 + enumeration_obj[DISPLAY_NAME].string.pointer); 148 + strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value, 149 + enumeration_obj[DEFAULT_VAL].string.pointer); 150 + strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier, 151 + enumeration_obj[MODIFIER].string.pointer); 152 + 153 + next_obj = MODIFIER + 1; 154 + 155 + value_modifier_count = (uintptr_t)enumeration_obj[next_obj].string.pointer; 156 + 157 + for (i = 0; i < value_modifier_count; i++) { 158 + strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, 159 + enumeration_obj[++next_obj].string.pointer); 160 + strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";"); 161 + } 162 + 163 + possible_values_count = (uintptr_t) enumeration_obj[++next_obj].string.pointer; 164 + 165 + for (i = 0; i < possible_values_count; i++) { 166 + strcat(wmi_priv.enumeration_data[instance_id].possible_values, 167 + enumeration_obj[++next_obj].string.pointer); 168 + strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";"); 169 + } 170 + 171 + return sysfs_create_group(attr_name_kobj, &enumeration_attr_group); 172 + } 173 + 174 + /** 175 + * exit_enum_attributes() - Clear all attribute data 176 + * 177 + * Clears all data allocated for this group of attributes 178 + */ 179 + void exit_enum_attributes(void) 180 + { 181 + int instance_id; 182 + 183 + for (instance_id = 0; instance_id < wmi_priv.enumeration_instances_count; instance_id++) { 184 + if (wmi_priv.enumeration_data[instance_id].attr_name_kobj) 185 + sysfs_remove_group(wmi_priv.enumeration_data[instance_id].attr_name_kobj, 186 + &enumeration_attr_group); 187 + } 188 + kfree(wmi_priv.enumeration_data); 189 + }
+179
drivers/platform/x86/dell-wmi-sysman/int-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to integer type attributes under BIOS Integer GUID for use with 4 + * dell-wmi-sysman 5 + * 6 + * Copyright (c) 2020 Dell Inc. 7 + */ 8 + 9 + #include "dell-wmi-sysman.h" 10 + 11 + enum int_properties {MIN_VALUE = 6, MAX_VALUE, SCALAR_INCR}; 12 + 13 + get_instance_id(integer); 14 + 15 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 16 + { 17 + int instance_id = get_integer_instance_id(kobj); 18 + union acpi_object *obj; 19 + ssize_t ret; 20 + 21 + if (instance_id < 0) 22 + return instance_id; 23 + 24 + /* need to use specific instance_id and guid combination to get right data */ 25 + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); 26 + if (!obj) 27 + return -EIO; 28 + if (obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_INTEGER) { 29 + kfree(obj); 30 + return -EINVAL; 31 + } 32 + ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[CURRENT_VAL].integer.value); 33 + kfree(obj); 34 + return ret; 35 + } 36 + 37 + /** 38 + * validate_integer_input() - Validate input of current_value against lower and upper bound 39 + * @instance_id: The instance on which input is validated 40 + * @buf: Input value 41 + */ 42 + static int validate_integer_input(int instance_id, char *buf) 43 + { 44 + int in_val; 45 + int ret; 46 + 47 + ret = kstrtoint(buf, 0, &in_val); 48 + if (ret) 49 + return ret; 50 + if (in_val < wmi_priv.integer_data[instance_id].min_value || 51 + in_val > wmi_priv.integer_data[instance_id].max_value) 52 + return -EINVAL; 53 + 54 + /* workaround for BIOS error. 55 + * validate input to avoid setting 0 when integer input passed with + sign 56 + */ 57 + if (*buf == '+') 58 + memmove(buf, (buf + 1), strlen(buf + 1) + 1); 59 + 60 + return ret; 61 + } 62 + 63 + attribute_s_property_show(display_name_language_code, integer); 64 + static struct kobj_attribute integer_displ_langcode = 65 + __ATTR_RO(display_name_language_code); 66 + 67 + attribute_s_property_show(display_name, integer); 68 + static struct kobj_attribute integer_displ_name = 69 + __ATTR_RO(display_name); 70 + 71 + attribute_n_property_show(default_value, integer); 72 + static struct kobj_attribute integer_default_val = 73 + __ATTR_RO(default_value); 74 + 75 + attribute_property_store(current_value, integer); 76 + static struct kobj_attribute integer_current_val = 77 + __ATTR_RW_MODE(current_value, 0600); 78 + 79 + attribute_s_property_show(dell_modifier, integer); 80 + static struct kobj_attribute integer_modifier = 81 + __ATTR_RO(dell_modifier); 82 + 83 + attribute_n_property_show(min_value, integer); 84 + static struct kobj_attribute integer_lower_bound = 85 + __ATTR_RO(min_value); 86 + 87 + attribute_n_property_show(max_value, integer); 88 + static struct kobj_attribute integer_upper_bound = 89 + __ATTR_RO(max_value); 90 + 91 + attribute_n_property_show(scalar_increment, integer); 92 + static struct kobj_attribute integer_scalar_increment = 93 + __ATTR_RO(scalar_increment); 94 + 95 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 96 + char *buf) 97 + { 98 + return sprintf(buf, "integer\n"); 99 + } 100 + static struct kobj_attribute integer_type = 101 + __ATTR_RO(type); 102 + 103 + static struct attribute *integer_attrs[] = { 104 + &integer_displ_langcode.attr, 105 + &integer_displ_name.attr, 106 + &integer_default_val.attr, 107 + &integer_current_val.attr, 108 + &integer_modifier.attr, 109 + &integer_lower_bound.attr, 110 + &integer_upper_bound.attr, 111 + &integer_scalar_increment.attr, 112 + &integer_type.attr, 113 + NULL, 114 + }; 115 + 116 + static const struct attribute_group integer_attr_group = { 117 + .attrs = integer_attrs, 118 + }; 119 + 120 + int alloc_int_data(void) 121 + { 122 + int ret = 0; 123 + 124 + wmi_priv.integer_instances_count = get_instance_count(DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); 125 + wmi_priv.integer_data = kcalloc(wmi_priv.integer_instances_count, 126 + sizeof(struct integer_data), GFP_KERNEL); 127 + if (!wmi_priv.integer_data) { 128 + wmi_priv.integer_instances_count = 0; 129 + ret = -ENOMEM; 130 + } 131 + return ret; 132 + } 133 + 134 + /** 135 + * populate_int_data() - Populate all properties of an instance under integer attribute 136 + * @integer_obj: ACPI object with integer data 137 + * @instance_id: The instance to enumerate 138 + * @attr_name_kobj: The parent kernel object 139 + */ 140 + int populate_int_data(union acpi_object *integer_obj, int instance_id, 141 + struct kobject *attr_name_kobj) 142 + { 143 + wmi_priv.integer_data[instance_id].attr_name_kobj = attr_name_kobj; 144 + strlcpy_attr(wmi_priv.integer_data[instance_id].attribute_name, 145 + integer_obj[ATTR_NAME].string.pointer); 146 + strlcpy_attr(wmi_priv.integer_data[instance_id].display_name_language_code, 147 + integer_obj[DISPL_NAME_LANG_CODE].string.pointer); 148 + strlcpy_attr(wmi_priv.integer_data[instance_id].display_name, 149 + integer_obj[DISPLAY_NAME].string.pointer); 150 + wmi_priv.integer_data[instance_id].default_value = 151 + (uintptr_t)integer_obj[DEFAULT_VAL].string.pointer; 152 + strlcpy_attr(wmi_priv.integer_data[instance_id].dell_modifier, 153 + integer_obj[MODIFIER].string.pointer); 154 + wmi_priv.integer_data[instance_id].min_value = 155 + (uintptr_t)integer_obj[MIN_VALUE].string.pointer; 156 + wmi_priv.integer_data[instance_id].max_value = 157 + (uintptr_t)integer_obj[MAX_VALUE].string.pointer; 158 + wmi_priv.integer_data[instance_id].scalar_increment = 159 + (uintptr_t)integer_obj[SCALAR_INCR].string.pointer; 160 + 161 + return sysfs_create_group(attr_name_kobj, &integer_attr_group); 162 + } 163 + 164 + /** 165 + * exit_int_attributes() - Clear all attribute data 166 + * 167 + * Clears all data allocated for this group of attributes 168 + */ 169 + void exit_int_attributes(void) 170 + { 171 + int instance_id; 172 + 173 + for (instance_id = 0; instance_id < wmi_priv.integer_instances_count; instance_id++) { 174 + if (wmi_priv.integer_data[instance_id].attr_name_kobj) 175 + sysfs_remove_group(wmi_priv.integer_data[instance_id].attr_name_kobj, 176 + &integer_attr_group); 177 + } 178 + kfree(wmi_priv.integer_data); 179 + }
+187
drivers/platform/x86/dell-wmi-sysman/passobj-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to password object type attributes under BIOS Password Object GUID for 4 + * use with dell-wmi-sysman 5 + * 6 + * Copyright (c) 2020 Dell Inc. 7 + */ 8 + 9 + #include "dell-wmi-sysman.h" 10 + 11 + enum po_properties {IS_PASS_SET = 1, MIN_PASS_LEN, MAX_PASS_LEN}; 12 + 13 + get_instance_id(po); 14 + 15 + static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, 16 + char *buf) 17 + { 18 + int instance_id = get_po_instance_id(kobj); 19 + union acpi_object *obj; 20 + ssize_t ret; 21 + 22 + if (instance_id < 0) 23 + return instance_id; 24 + 25 + /* need to use specific instance_id and guid combination to get right data */ 26 + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); 27 + if (!obj) 28 + return -EIO; 29 + if (obj->package.elements[IS_PASS_SET].type != ACPI_TYPE_INTEGER) { 30 + kfree(obj); 31 + return -EINVAL; 32 + } 33 + ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[IS_PASS_SET].integer.value); 34 + kfree(obj); 35 + return ret; 36 + } 37 + 38 + static struct kobj_attribute po_is_pass_set = __ATTR_RO(is_enabled); 39 + 40 + static ssize_t current_password_store(struct kobject *kobj, 41 + struct kobj_attribute *attr, 42 + const char *buf, size_t count) 43 + { 44 + char *target = NULL; 45 + int length; 46 + 47 + length = strlen(buf); 48 + if (buf[length-1] == '\n') 49 + length--; 50 + 51 + /* firmware does verifiation of min/max password length, 52 + * hence only check for not exceeding MAX_BUFF here. 53 + */ 54 + if (length >= MAX_BUFF) 55 + return -EINVAL; 56 + 57 + if (strcmp(kobj->name, "Admin") == 0) 58 + target = wmi_priv.current_admin_password; 59 + else if (strcmp(kobj->name, "System") == 0) 60 + target = wmi_priv.current_system_password; 61 + if (!target) 62 + return -EIO; 63 + memcpy(target, buf, length); 64 + target[length] = '\0'; 65 + 66 + return count; 67 + } 68 + 69 + static struct kobj_attribute po_current_password = __ATTR_WO(current_password); 70 + 71 + static ssize_t new_password_store(struct kobject *kobj, 72 + struct kobj_attribute *attr, 73 + const char *buf, size_t count) 74 + { 75 + char *p, *buf_cp; 76 + int ret; 77 + 78 + buf_cp = kstrdup(buf, GFP_KERNEL); 79 + if (!buf_cp) 80 + return -ENOMEM; 81 + p = memchr(buf_cp, '\n', count); 82 + 83 + if (p != NULL) 84 + *p = '\0'; 85 + if (strlen(buf_cp) > MAX_BUFF) { 86 + ret = -EINVAL; 87 + goto out; 88 + } 89 + 90 + ret = set_new_password(kobj->name, buf_cp); 91 + 92 + out: 93 + kfree(buf_cp); 94 + return ret ? ret : count; 95 + } 96 + 97 + static struct kobj_attribute po_new_password = __ATTR_WO(new_password); 98 + 99 + attribute_n_property_show(min_password_length, po); 100 + static struct kobj_attribute po_min_pass_length = __ATTR_RO(min_password_length); 101 + 102 + attribute_n_property_show(max_password_length, po); 103 + static struct kobj_attribute po_max_pass_length = __ATTR_RO(max_password_length); 104 + 105 + static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, 106 + char *buf) 107 + { 108 + return sprintf(buf, "password\n"); 109 + } 110 + 111 + static struct kobj_attribute po_mechanism = __ATTR_RO(mechanism); 112 + 113 + static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, 114 + char *buf) 115 + { 116 + if (strcmp(kobj->name, "Admin") == 0) 117 + return sprintf(buf, "bios-admin\n"); 118 + else if (strcmp(kobj->name, "System") == 0) 119 + return sprintf(buf, "power-on\n"); 120 + return -EIO; 121 + } 122 + 123 + static struct kobj_attribute po_role = __ATTR_RO(role); 124 + 125 + static struct attribute *po_attrs[] = { 126 + &po_is_pass_set.attr, 127 + &po_min_pass_length.attr, 128 + &po_max_pass_length.attr, 129 + &po_current_password.attr, 130 + &po_new_password.attr, 131 + &po_role.attr, 132 + &po_mechanism.attr, 133 + NULL, 134 + }; 135 + 136 + static const struct attribute_group po_attr_group = { 137 + .attrs = po_attrs, 138 + }; 139 + 140 + int alloc_po_data(void) 141 + { 142 + int ret = 0; 143 + 144 + wmi_priv.po_instances_count = get_instance_count(DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); 145 + wmi_priv.po_data = kcalloc(wmi_priv.po_instances_count, sizeof(struct po_data), GFP_KERNEL); 146 + if (!wmi_priv.po_data) { 147 + wmi_priv.po_instances_count = 0; 148 + ret = -ENOMEM; 149 + } 150 + return ret; 151 + } 152 + 153 + /** 154 + * populate_po_data() - Populate all properties of an instance under password object attribute 155 + * @po_obj: ACPI object with password object data 156 + * @instance_id: The instance to enumerate 157 + * @attr_name_kobj: The parent kernel object 158 + */ 159 + int populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj) 160 + { 161 + wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj; 162 + strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name, 163 + po_obj[ATTR_NAME].string.pointer); 164 + wmi_priv.po_data[instance_id].min_password_length = 165 + (uintptr_t)po_obj[MIN_PASS_LEN].string.pointer; 166 + wmi_priv.po_data[instance_id].max_password_length = 167 + (uintptr_t) po_obj[MAX_PASS_LEN].string.pointer; 168 + 169 + return sysfs_create_group(attr_name_kobj, &po_attr_group); 170 + } 171 + 172 + /** 173 + * exit_po_attributes() - Clear all attribute data 174 + * 175 + * Clears all data allocated for this group of attributes 176 + */ 177 + void exit_po_attributes(void) 178 + { 179 + int instance_id; 180 + 181 + for (instance_id = 0; instance_id < wmi_priv.po_instances_count; instance_id++) { 182 + if (wmi_priv.po_data[instance_id].attr_name_kobj) 183 + sysfs_remove_group(wmi_priv.po_data[instance_id].attr_name_kobj, 184 + &po_attr_group); 185 + } 186 + kfree(wmi_priv.po_data); 187 + }
+153
drivers/platform/x86/dell-wmi-sysman/passwordattr-interface.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to SET password methods under BIOS attributes interface GUID 4 + * 5 + * Copyright (c) 2020 Dell Inc. 6 + */ 7 + 8 + #include <linux/wmi.h> 9 + #include "dell-wmi-sysman.h" 10 + 11 + static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size) 12 + { 13 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 14 + struct acpi_buffer input; 15 + union acpi_object *obj; 16 + acpi_status status; 17 + int ret = -EIO; 18 + 19 + input.length = (acpi_size) size; 20 + input.pointer = in_args; 21 + status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); 22 + if (ACPI_FAILURE(status)) 23 + return -EIO; 24 + obj = (union acpi_object *)output.pointer; 25 + if (obj->type == ACPI_TYPE_INTEGER) 26 + ret = obj->integer.value; 27 + 28 + kfree(output.pointer); 29 + /* let userland know it may need to check is_password_set again */ 30 + kobject_uevent(&wmi_priv.class_dev->kobj, KOBJ_CHANGE); 31 + return map_wmi_error(ret); 32 + } 33 + 34 + /** 35 + * set_new_password() - Sets a system admin password 36 + * @password_type: The type of password to set 37 + * @new: The new password 38 + * 39 + * Sets the password using plaintext interface 40 + */ 41 + int set_new_password(const char *password_type, const char *new) 42 + { 43 + size_t password_type_size, current_password_size, new_size; 44 + size_t security_area_size, buffer_size; 45 + char *buffer = NULL, *start; 46 + char *current_password; 47 + int ret; 48 + 49 + mutex_lock(&wmi_priv.mutex); 50 + if (!wmi_priv.password_attr_wdev) { 51 + ret = -ENODEV; 52 + goto out; 53 + } 54 + if (strcmp(password_type, "Admin") == 0) { 55 + current_password = wmi_priv.current_admin_password; 56 + } else if (strcmp(password_type, "System") == 0) { 57 + current_password = wmi_priv.current_system_password; 58 + } else { 59 + ret = -EINVAL; 60 + dev_err(&wmi_priv.password_attr_wdev->dev, "unknown password type %s\n", 61 + password_type); 62 + goto out; 63 + } 64 + 65 + /* build/calculate buffer */ 66 + security_area_size = calculate_security_buffer(wmi_priv.current_admin_password); 67 + password_type_size = calculate_string_buffer(password_type); 68 + current_password_size = calculate_string_buffer(current_password); 69 + new_size = calculate_string_buffer(new); 70 + buffer_size = security_area_size + password_type_size + current_password_size + new_size; 71 + buffer = kzalloc(buffer_size, GFP_KERNEL); 72 + if (!buffer) { 73 + ret = -ENOMEM; 74 + goto out; 75 + } 76 + 77 + /* build security area */ 78 + populate_security_buffer(buffer, wmi_priv.current_admin_password); 79 + 80 + /* build variables to set */ 81 + start = buffer + security_area_size; 82 + ret = populate_string_buffer(start, password_type_size, password_type); 83 + if (ret < 0) 84 + goto out; 85 + 86 + start += ret; 87 + ret = populate_string_buffer(start, current_password_size, current_password); 88 + if (ret < 0) 89 + goto out; 90 + 91 + start += ret; 92 + ret = populate_string_buffer(start, new_size, new); 93 + if (ret < 0) 94 + goto out; 95 + 96 + print_hex_dump_bytes("set new password data: ", DUMP_PREFIX_NONE, buffer, buffer_size); 97 + ret = call_password_interface(wmi_priv.password_attr_wdev, buffer, buffer_size); 98 + /* clear current_password here and use user input from wmi_priv.current_password */ 99 + if (!ret) 100 + memset(current_password, 0, MAX_BUFF); 101 + /* explain to user the detailed failure reason */ 102 + else if (ret == -EOPNOTSUPP) 103 + dev_err(&wmi_priv.password_attr_wdev->dev, "admin password must be configured\n"); 104 + else if (ret == -EACCES) 105 + dev_err(&wmi_priv.password_attr_wdev->dev, "invalid password\n"); 106 + 107 + out: 108 + kfree(buffer); 109 + mutex_unlock(&wmi_priv.mutex); 110 + 111 + return ret; 112 + } 113 + 114 + static int bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *context) 115 + { 116 + mutex_lock(&wmi_priv.mutex); 117 + wmi_priv.password_attr_wdev = wdev; 118 + mutex_unlock(&wmi_priv.mutex); 119 + return 0; 120 + } 121 + 122 + static int bios_attr_pass_interface_remove(struct wmi_device *wdev) 123 + { 124 + mutex_lock(&wmi_priv.mutex); 125 + wmi_priv.password_attr_wdev = NULL; 126 + mutex_unlock(&wmi_priv.mutex); 127 + return 0; 128 + } 129 + 130 + static const struct wmi_device_id bios_attr_pass_interface_id_table[] = { 131 + { .guid_string = DELL_WMI_BIOS_PASSWORD_INTERFACE_GUID }, 132 + { }, 133 + }; 134 + static struct wmi_driver bios_attr_pass_interface_driver = { 135 + .driver = { 136 + .name = DRIVER_NAME"-password" 137 + }, 138 + .probe = bios_attr_pass_interface_probe, 139 + .remove = bios_attr_pass_interface_remove, 140 + .id_table = bios_attr_pass_interface_id_table, 141 + }; 142 + 143 + int init_bios_attr_pass_interface(void) 144 + { 145 + return wmi_driver_register(&bios_attr_pass_interface_driver); 146 + } 147 + 148 + void exit_bios_attr_pass_interface(void) 149 + { 150 + wmi_driver_unregister(&bios_attr_pass_interface_driver); 151 + } 152 + 153 + MODULE_DEVICE_TABLE(wmi, bios_attr_pass_interface_id_table);
+159
drivers/platform/x86/dell-wmi-sysman/string-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to string type attributes under BIOS String GUID for use with 4 + * dell-wmi-sysman 5 + * 6 + * Copyright (c) 2020 Dell Inc. 7 + */ 8 + 9 + #include "dell-wmi-sysman.h" 10 + 11 + enum string_properties {MIN_LEN = 6, MAX_LEN}; 12 + 13 + get_instance_id(str); 14 + 15 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 16 + { 17 + int instance_id = get_str_instance_id(kobj); 18 + union acpi_object *obj; 19 + ssize_t ret; 20 + 21 + if (instance_id < 0) 22 + return -EIO; 23 + 24 + /* need to use specific instance_id and guid combination to get right data */ 25 + obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); 26 + if (!obj) 27 + return -EIO; 28 + if (obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_STRING) { 29 + kfree(obj); 30 + return -EINVAL; 31 + } 32 + ret = snprintf(buf, PAGE_SIZE, "%s\n", obj->package.elements[CURRENT_VAL].string.pointer); 33 + kfree(obj); 34 + return ret; 35 + } 36 + 37 + /** 38 + * validate_str_input() - Validate input of current_value against min and max lengths 39 + * @instance_id: The instance on which input is validated 40 + * @buf: Input value 41 + */ 42 + static int validate_str_input(int instance_id, const char *buf) 43 + { 44 + int in_len = strlen(buf); 45 + 46 + if ((in_len < wmi_priv.str_data[instance_id].min_length) || 47 + (in_len > wmi_priv.str_data[instance_id].max_length)) 48 + return -EINVAL; 49 + 50 + return 0; 51 + } 52 + 53 + attribute_s_property_show(display_name_language_code, str); 54 + static struct kobj_attribute str_displ_langcode = 55 + __ATTR_RO(display_name_language_code); 56 + 57 + attribute_s_property_show(display_name, str); 58 + static struct kobj_attribute str_displ_name = 59 + __ATTR_RO(display_name); 60 + 61 + attribute_s_property_show(default_value, str); 62 + static struct kobj_attribute str_default_val = 63 + __ATTR_RO(default_value); 64 + 65 + attribute_property_store(current_value, str); 66 + static struct kobj_attribute str_current_val = 67 + __ATTR_RW_MODE(current_value, 0600); 68 + 69 + attribute_s_property_show(dell_modifier, str); 70 + static struct kobj_attribute str_modifier = 71 + __ATTR_RO(dell_modifier); 72 + 73 + attribute_n_property_show(min_length, str); 74 + static struct kobj_attribute str_min_length = 75 + __ATTR_RO(min_length); 76 + 77 + attribute_n_property_show(max_length, str); 78 + static struct kobj_attribute str_max_length = 79 + __ATTR_RO(max_length); 80 + 81 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 82 + char *buf) 83 + { 84 + return sprintf(buf, "string\n"); 85 + } 86 + static struct kobj_attribute str_type = 87 + __ATTR_RO(type); 88 + 89 + static struct attribute *str_attrs[] = { 90 + &str_displ_langcode.attr, 91 + &str_displ_name.attr, 92 + &str_default_val.attr, 93 + &str_current_val.attr, 94 + &str_modifier.attr, 95 + &str_min_length.attr, 96 + &str_max_length.attr, 97 + &str_type.attr, 98 + NULL, 99 + }; 100 + 101 + static const struct attribute_group str_attr_group = { 102 + .attrs = str_attrs, 103 + }; 104 + 105 + int alloc_str_data(void) 106 + { 107 + int ret = 0; 108 + 109 + wmi_priv.str_instances_count = get_instance_count(DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); 110 + wmi_priv.str_data = kcalloc(wmi_priv.str_instances_count, 111 + sizeof(struct str_data), GFP_KERNEL); 112 + if (!wmi_priv.str_data) { 113 + wmi_priv.str_instances_count = 0; 114 + ret = -ENOMEM; 115 + } 116 + return ret; 117 + } 118 + 119 + /** 120 + * populate_str_data() - Populate all properties of an instance under string attribute 121 + * @str_obj: ACPI object with integer data 122 + * @instance_id: The instance to enumerate 123 + * @attr_name_kobj: The parent kernel object 124 + */ 125 + int populate_str_data(union acpi_object *str_obj, int instance_id, struct kobject *attr_name_kobj) 126 + { 127 + wmi_priv.str_data[instance_id].attr_name_kobj = attr_name_kobj; 128 + strlcpy_attr(wmi_priv.str_data[instance_id].attribute_name, 129 + str_obj[ATTR_NAME].string.pointer); 130 + strlcpy_attr(wmi_priv.str_data[instance_id].display_name_language_code, 131 + str_obj[DISPL_NAME_LANG_CODE].string.pointer); 132 + strlcpy_attr(wmi_priv.str_data[instance_id].display_name, 133 + str_obj[DISPLAY_NAME].string.pointer); 134 + strlcpy_attr(wmi_priv.str_data[instance_id].default_value, 135 + str_obj[DEFAULT_VAL].string.pointer); 136 + strlcpy_attr(wmi_priv.str_data[instance_id].dell_modifier, 137 + str_obj[MODIFIER].string.pointer); 138 + wmi_priv.str_data[instance_id].min_length = (uintptr_t)str_obj[MIN_LEN].string.pointer; 139 + wmi_priv.str_data[instance_id].max_length = (uintptr_t) str_obj[MAX_LEN].string.pointer; 140 + 141 + return sysfs_create_group(attr_name_kobj, &str_attr_group); 142 + } 143 + 144 + /** 145 + * exit_str_attributes() - Clear all attribute data 146 + * 147 + * Clears all data allocated for this group of attributes 148 + */ 149 + void exit_str_attributes(void) 150 + { 151 + int instance_id; 152 + 153 + for (instance_id = 0; instance_id < wmi_priv.str_instances_count; instance_id++) { 154 + if (wmi_priv.str_data[instance_id].attr_name_kobj) 155 + sysfs_remove_group(wmi_priv.str_data[instance_id].attr_name_kobj, 156 + &str_attr_group); 157 + } 158 + kfree(wmi_priv.str_data); 159 + }
+627
drivers/platform/x86/dell-wmi-sysman/sysman.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Common methods for use with dell-wmi-sysman 4 + * 5 + * Copyright (c) 2020 Dell Inc. 6 + */ 7 + 8 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 + 10 + #include <linux/fs.h> 11 + #include <linux/dmi.h> 12 + #include <linux/module.h> 13 + #include <linux/kernel.h> 14 + #include <linux/wmi.h> 15 + #include "dell-wmi-sysman.h" 16 + 17 + #define MAX_TYPES 4 18 + #include <linux/nls.h> 19 + 20 + static struct class firmware_attributes_class = { 21 + .name = "firmware-attributes", 22 + }; 23 + 24 + struct wmi_sysman_priv wmi_priv = { 25 + .mutex = __MUTEX_INITIALIZER(wmi_priv.mutex), 26 + }; 27 + 28 + /* reset bios to defaults */ 29 + static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"}; 30 + static int reset_option = -1; 31 + 32 + 33 + /** 34 + * populate_string_buffer() - populates a string buffer 35 + * @buffer: the start of the destination buffer 36 + * @buffer_len: length of the destination buffer 37 + * @str: the string to insert into buffer 38 + */ 39 + ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str) 40 + { 41 + u16 *length = (u16 *)buffer; 42 + u16 *target = length + 1; 43 + int ret; 44 + 45 + ret = utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN, 46 + target, buffer_len - sizeof(u16)); 47 + if (ret < 0) { 48 + dev_err(wmi_priv.class_dev, "UTF16 conversion failed\n"); 49 + return ret; 50 + } 51 + 52 + if ((ret * sizeof(u16)) > U16_MAX) { 53 + dev_err(wmi_priv.class_dev, "Error string too long\n"); 54 + return -ERANGE; 55 + } 56 + 57 + *length = ret * sizeof(u16); 58 + return sizeof(u16) + *length; 59 + } 60 + 61 + /** 62 + * calculate_string_buffer() - determines size of string buffer for use with BIOS communication 63 + * @str: the string to calculate based upon 64 + * 65 + */ 66 + size_t calculate_string_buffer(const char *str) 67 + { 68 + /* u16 length field + one UTF16 char for each input char */ 69 + return sizeof(u16) + strlen(str) * sizeof(u16); 70 + } 71 + 72 + /** 73 + * calculate_security_buffer() - determines size of security buffer for authentication scheme 74 + * @authentication: the authentication content 75 + * 76 + * Currently only supported type is Admin password 77 + */ 78 + size_t calculate_security_buffer(char *authentication) 79 + { 80 + if (strlen(authentication) > 0) { 81 + return (sizeof(u32) * 2) + strlen(authentication) + 82 + strlen(authentication) % 2; 83 + } 84 + return sizeof(u32) * 2; 85 + } 86 + 87 + /** 88 + * populate_security_buffer() - builds a security buffer for authentication scheme 89 + * @buffer: the buffer to populate 90 + * @authentication: the authentication content 91 + * 92 + * Currently only supported type is PLAIN TEXT 93 + */ 94 + void populate_security_buffer(char *buffer, char *authentication) 95 + { 96 + char *auth = buffer + sizeof(u32) * 2; 97 + u32 *sectype = (u32 *) buffer; 98 + u32 *seclen = sectype + 1; 99 + 100 + *sectype = strlen(authentication) > 0 ? 1 : 0; 101 + *seclen = strlen(authentication); 102 + 103 + /* plain text */ 104 + if (strlen(authentication) > 0) 105 + memcpy(auth, authentication, *seclen); 106 + } 107 + 108 + /** 109 + * map_wmi_error() - map errors from WMI methods to kernel error codes 110 + * @error_code: integer error code returned from Dell's firmware 111 + */ 112 + int map_wmi_error(int error_code) 113 + { 114 + switch (error_code) { 115 + case 0: 116 + /* success */ 117 + return 0; 118 + case 1: 119 + /* failed */ 120 + return -EIO; 121 + case 2: 122 + /* invalid parameter */ 123 + return -EINVAL; 124 + case 3: 125 + /* access denied */ 126 + return -EACCES; 127 + case 4: 128 + /* not supported */ 129 + return -EOPNOTSUPP; 130 + case 5: 131 + /* memory error */ 132 + return -ENOMEM; 133 + case 6: 134 + /* protocol error */ 135 + return -EPROTO; 136 + } 137 + /* unspecified error */ 138 + return -EIO; 139 + } 140 + 141 + /** 142 + * reset_bios_show() - sysfs implementaton for read reset_bios 143 + * @kobj: Kernel object for this attribute 144 + * @attr: Kernel object attribute 145 + * @buf: The buffer to display to userspace 146 + */ 147 + static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 148 + { 149 + char *start = buf; 150 + int i; 151 + 152 + for (i = 0; i < MAX_TYPES; i++) { 153 + if (i == reset_option) 154 + buf += sprintf(buf, "[%s] ", reset_types[i]); 155 + else 156 + buf += sprintf(buf, "%s ", reset_types[i]); 157 + } 158 + buf += sprintf(buf, "\n"); 159 + return buf-start; 160 + } 161 + 162 + /** 163 + * reset_bios_store() - sysfs implementaton for write reset_bios 164 + * @kobj: Kernel object for this attribute 165 + * @attr: Kernel object attribute 166 + * @buf: The buffer from userspace 167 + * @count: the size of the buffer from userspace 168 + */ 169 + static ssize_t reset_bios_store(struct kobject *kobj, 170 + struct kobj_attribute *attr, const char *buf, size_t count) 171 + { 172 + int type = sysfs_match_string(reset_types, buf); 173 + int ret; 174 + 175 + if (type < 0) 176 + return type; 177 + 178 + ret = set_bios_defaults(type); 179 + pr_debug("reset all attributes request type %d: %d\n", type, ret); 180 + if (!ret) { 181 + reset_option = type; 182 + ret = count; 183 + } 184 + 185 + return ret; 186 + } 187 + 188 + /** 189 + * pending_reboot_show() - sysfs implementaton for read pending_reboot 190 + * @kobj: Kernel object for this attribute 191 + * @attr: Kernel object attribute 192 + * @buf: The buffer to display to userspace 193 + * 194 + * Stores default value as 0 195 + * When current_value is changed this attribute is set to 1 to notify reboot may be required 196 + */ 197 + static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, 198 + char *buf) 199 + { 200 + return sprintf(buf, "%d\n", wmi_priv.pending_changes); 201 + } 202 + 203 + static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); 204 + static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); 205 + 206 + 207 + /** 208 + * create_attributes_level_sysfs_files() - Creates reset_bios and 209 + * pending_reboot attributes 210 + */ 211 + static int create_attributes_level_sysfs_files(void) 212 + { 213 + int ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr); 214 + 215 + if (ret) { 216 + pr_debug("could not create reset_bios file\n"); 217 + return ret; 218 + } 219 + 220 + ret = sysfs_create_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr); 221 + if (ret) { 222 + pr_debug("could not create changing_pending_reboot file\n"); 223 + sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr); 224 + } 225 + return ret; 226 + } 227 + 228 + static void release_reset_bios_data(void) 229 + { 230 + sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &reset_bios.attr); 231 + sysfs_remove_file(&wmi_priv.main_dir_kset->kobj, &pending_reboot.attr); 232 + } 233 + 234 + static ssize_t wmi_sysman_attr_show(struct kobject *kobj, struct attribute *attr, 235 + char *buf) 236 + { 237 + struct kobj_attribute *kattr; 238 + ssize_t ret = -EIO; 239 + 240 + kattr = container_of(attr, struct kobj_attribute, attr); 241 + if (kattr->show) 242 + ret = kattr->show(kobj, kattr, buf); 243 + return ret; 244 + } 245 + 246 + static ssize_t wmi_sysman_attr_store(struct kobject *kobj, struct attribute *attr, 247 + const char *buf, size_t count) 248 + { 249 + struct kobj_attribute *kattr; 250 + ssize_t ret = -EIO; 251 + 252 + kattr = container_of(attr, struct kobj_attribute, attr); 253 + if (kattr->store) 254 + ret = kattr->store(kobj, kattr, buf, count); 255 + return ret; 256 + } 257 + 258 + static const struct sysfs_ops wmi_sysman_kobj_sysfs_ops = { 259 + .show = wmi_sysman_attr_show, 260 + .store = wmi_sysman_attr_store, 261 + }; 262 + 263 + static void attr_name_release(struct kobject *kobj) 264 + { 265 + kfree(kobj); 266 + } 267 + 268 + static struct kobj_type attr_name_ktype = { 269 + .release = attr_name_release, 270 + .sysfs_ops = &wmi_sysman_kobj_sysfs_ops, 271 + }; 272 + 273 + /** 274 + * strlcpy_attr - Copy a length-limited, NULL-terminated string with bound checks 275 + * @dest: Where to copy the string to 276 + * @src: Where to copy the string from 277 + */ 278 + void strlcpy_attr(char *dest, char *src) 279 + { 280 + size_t len = strlen(src) + 1; 281 + 282 + if (len > 1 && len <= MAX_BUFF) 283 + strlcpy(dest, src, len); 284 + 285 + /*len can be zero because any property not-applicable to attribute can 286 + * be empty so check only for too long buffers and log error 287 + */ 288 + if (len > MAX_BUFF) 289 + pr_err("Source string returned from BIOS is out of bound!\n"); 290 + } 291 + 292 + /** 293 + * get_wmiobj_pointer() - Get Content of WMI block for particular instance 294 + * @instance_id: WMI instance ID 295 + * @guid_string: WMI GUID (in str form) 296 + * 297 + * Fetches the content for WMI block (instance_id) under GUID (guid_string) 298 + * Caller must kfree the return 299 + */ 300 + union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string) 301 + { 302 + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 303 + acpi_status status; 304 + 305 + status = wmi_query_block(guid_string, instance_id, &out); 306 + 307 + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; 308 + } 309 + 310 + /** 311 + * get_instance_count() - Compute total number of instances under guid_string 312 + * @guid_string: WMI GUID (in string form) 313 + */ 314 + int get_instance_count(const char *guid_string) 315 + { 316 + union acpi_object *wmi_obj = NULL; 317 + int i = 0; 318 + 319 + do { 320 + kfree(wmi_obj); 321 + wmi_obj = get_wmiobj_pointer(i, guid_string); 322 + i++; 323 + } while (wmi_obj); 324 + 325 + return (i-1); 326 + } 327 + 328 + /** 329 + * alloc_attributes_data() - Allocate attributes data for a particular type 330 + * @attr_type: Attribute type to allocate 331 + */ 332 + static int alloc_attributes_data(int attr_type) 333 + { 334 + int retval = 0; 335 + 336 + switch (attr_type) { 337 + case ENUM: 338 + retval = alloc_enum_data(); 339 + break; 340 + case INT: 341 + retval = alloc_int_data(); 342 + break; 343 + case STR: 344 + retval = alloc_str_data(); 345 + break; 346 + case PO: 347 + retval = alloc_po_data(); 348 + break; 349 + default: 350 + break; 351 + } 352 + 353 + return retval; 354 + } 355 + 356 + /** 357 + * destroy_attribute_objs() - Free a kset of kobjects 358 + * @kset: The kset to destroy 359 + * 360 + * Fress kobjects created for each attribute_name under attribute type kset 361 + */ 362 + static void destroy_attribute_objs(struct kset *kset) 363 + { 364 + struct kobject *pos, *next; 365 + 366 + list_for_each_entry_safe(pos, next, &kset->list, entry) { 367 + kobject_put(pos); 368 + } 369 + } 370 + 371 + /** 372 + * release_attributes_data() - Clean-up all sysfs directories and files created 373 + */ 374 + static void release_attributes_data(void) 375 + { 376 + release_reset_bios_data(); 377 + 378 + mutex_lock(&wmi_priv.mutex); 379 + exit_enum_attributes(); 380 + exit_int_attributes(); 381 + exit_str_attributes(); 382 + exit_po_attributes(); 383 + if (wmi_priv.authentication_dir_kset) { 384 + destroy_attribute_objs(wmi_priv.authentication_dir_kset); 385 + kset_unregister(wmi_priv.authentication_dir_kset); 386 + wmi_priv.authentication_dir_kset = NULL; 387 + } 388 + if (wmi_priv.main_dir_kset) { 389 + destroy_attribute_objs(wmi_priv.main_dir_kset); 390 + kset_unregister(wmi_priv.main_dir_kset); 391 + } 392 + mutex_unlock(&wmi_priv.mutex); 393 + 394 + } 395 + 396 + /** 397 + * init_bios_attributes() - Initialize all attributes for a type 398 + * @attr_type: The attribute type to initialize 399 + * @guid: The WMI GUID associated with this type to initialize 400 + * 401 + * Initialiaze all 4 types of attributes enumeration, integer, string and password object. 402 + * Populates each attrbute typ's respective properties under sysfs files 403 + */ 404 + static int init_bios_attributes(int attr_type, const char *guid) 405 + { 406 + struct kobject *attr_name_kobj; //individual attribute names 407 + union acpi_object *obj = NULL; 408 + union acpi_object *elements; 409 + struct kset *tmp_set; 410 + 411 + /* instance_id needs to be reset for each type GUID 412 + * also, instance IDs are unique within GUID but not across 413 + */ 414 + int instance_id = 0; 415 + int retval = 0; 416 + 417 + retval = alloc_attributes_data(attr_type); 418 + if (retval) 419 + return retval; 420 + /* need to use specific instance_id and guid combination to get right data */ 421 + obj = get_wmiobj_pointer(instance_id, guid); 422 + if (!obj) 423 + return -ENODEV; 424 + elements = obj->package.elements; 425 + 426 + mutex_lock(&wmi_priv.mutex); 427 + while (elements) { 428 + /* sanity checking */ 429 + if (strlen(elements[ATTR_NAME].string.pointer) == 0) { 430 + pr_debug("empty attribute found\n"); 431 + goto nextobj; 432 + } 433 + if (attr_type == PO) 434 + tmp_set = wmi_priv.authentication_dir_kset; 435 + else 436 + tmp_set = wmi_priv.main_dir_kset; 437 + 438 + if (kset_find_obj(tmp_set, elements[ATTR_NAME].string.pointer)) { 439 + pr_debug("duplicate attribute name found - %s\n", 440 + elements[ATTR_NAME].string.pointer); 441 + goto nextobj; 442 + } 443 + 444 + /* build attribute */ 445 + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); 446 + if (!attr_name_kobj) { 447 + retval = -ENOMEM; 448 + goto err_attr_init; 449 + } 450 + 451 + attr_name_kobj->kset = tmp_set; 452 + 453 + retval = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", 454 + elements[ATTR_NAME].string.pointer); 455 + if (retval) { 456 + kobject_put(attr_name_kobj); 457 + goto err_attr_init; 458 + } 459 + 460 + /* enumerate all of this attribute */ 461 + switch (attr_type) { 462 + case ENUM: 463 + retval = populate_enum_data(elements, instance_id, attr_name_kobj); 464 + break; 465 + case INT: 466 + retval = populate_int_data(elements, instance_id, attr_name_kobj); 467 + break; 468 + case STR: 469 + retval = populate_str_data(elements, instance_id, attr_name_kobj); 470 + break; 471 + case PO: 472 + retval = populate_po_data(elements, instance_id, attr_name_kobj); 473 + break; 474 + default: 475 + break; 476 + } 477 + 478 + if (retval) { 479 + pr_debug("failed to populate %s\n", 480 + elements[ATTR_NAME].string.pointer); 481 + goto err_attr_init; 482 + } 483 + 484 + nextobj: 485 + kfree(obj); 486 + instance_id++; 487 + obj = get_wmiobj_pointer(instance_id, guid); 488 + elements = obj ? obj->package.elements : NULL; 489 + } 490 + 491 + mutex_unlock(&wmi_priv.mutex); 492 + return 0; 493 + 494 + err_attr_init: 495 + mutex_unlock(&wmi_priv.mutex); 496 + release_attributes_data(); 497 + kfree(obj); 498 + return retval; 499 + } 500 + 501 + static int __init sysman_init(void) 502 + { 503 + int ret = 0; 504 + 505 + if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) && 506 + !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) { 507 + pr_err("Unable to run on non-Dell system\n"); 508 + return -ENODEV; 509 + } 510 + 511 + ret = init_bios_attr_set_interface(); 512 + if (ret || !wmi_priv.bios_attr_wdev) { 513 + pr_debug("failed to initialize set interface\n"); 514 + goto fail_set_interface; 515 + } 516 + 517 + ret = init_bios_attr_pass_interface(); 518 + if (ret || !wmi_priv.password_attr_wdev) { 519 + pr_debug("failed to initialize pass interface\n"); 520 + goto fail_pass_interface; 521 + } 522 + 523 + ret = class_register(&firmware_attributes_class); 524 + if (ret) 525 + goto fail_class; 526 + 527 + wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), 528 + NULL, "%s", DRIVER_NAME); 529 + if (IS_ERR(wmi_priv.class_dev)) { 530 + ret = PTR_ERR(wmi_priv.class_dev); 531 + goto fail_classdev; 532 + } 533 + 534 + wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL, 535 + &wmi_priv.class_dev->kobj); 536 + if (!wmi_priv.main_dir_kset) { 537 + ret = -ENOMEM; 538 + goto fail_main_kset; 539 + } 540 + 541 + wmi_priv.authentication_dir_kset = kset_create_and_add("authentication", NULL, 542 + &wmi_priv.class_dev->kobj); 543 + if (!wmi_priv.authentication_dir_kset) { 544 + ret = -ENOMEM; 545 + goto fail_authentication_kset; 546 + } 547 + 548 + ret = create_attributes_level_sysfs_files(); 549 + if (ret) { 550 + pr_debug("could not create reset BIOS attribute\n"); 551 + goto fail_reset_bios; 552 + } 553 + 554 + ret = init_bios_attributes(ENUM, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID); 555 + if (ret) { 556 + pr_debug("failed to populate enumeration type attributes\n"); 557 + goto fail_create_group; 558 + } 559 + 560 + ret = init_bios_attributes(INT, DELL_WMI_BIOS_INTEGER_ATTRIBUTE_GUID); 561 + if (ret) { 562 + pr_debug("failed to populate integer type attributes\n"); 563 + goto fail_create_group; 564 + } 565 + 566 + ret = init_bios_attributes(STR, DELL_WMI_BIOS_STRING_ATTRIBUTE_GUID); 567 + if (ret) { 568 + pr_debug("failed to populate string type attributes\n"); 569 + goto fail_create_group; 570 + } 571 + 572 + ret = init_bios_attributes(PO, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID); 573 + if (ret) { 574 + pr_debug("failed to populate pass object type attributes\n"); 575 + goto fail_create_group; 576 + } 577 + 578 + return 0; 579 + 580 + fail_create_group: 581 + release_attributes_data(); 582 + 583 + fail_reset_bios: 584 + if (wmi_priv.authentication_dir_kset) { 585 + kset_unregister(wmi_priv.authentication_dir_kset); 586 + wmi_priv.authentication_dir_kset = NULL; 587 + } 588 + 589 + fail_authentication_kset: 590 + if (wmi_priv.main_dir_kset) { 591 + kset_unregister(wmi_priv.main_dir_kset); 592 + wmi_priv.main_dir_kset = NULL; 593 + } 594 + 595 + fail_main_kset: 596 + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); 597 + 598 + fail_classdev: 599 + class_unregister(&firmware_attributes_class); 600 + 601 + fail_class: 602 + exit_bios_attr_pass_interface(); 603 + 604 + fail_pass_interface: 605 + exit_bios_attr_set_interface(); 606 + 607 + fail_set_interface: 608 + return ret; 609 + } 610 + 611 + static void __exit sysman_exit(void) 612 + { 613 + release_attributes_data(); 614 + device_destroy(&firmware_attributes_class, MKDEV(0, 0)); 615 + class_unregister(&firmware_attributes_class); 616 + exit_bios_attr_set_interface(); 617 + exit_bios_attr_pass_interface(); 618 + } 619 + 620 + module_init(sysman_init); 621 + module_exit(sysman_exit); 622 + 623 + MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 624 + MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>"); 625 + MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>"); 626 + MODULE_DESCRIPTION("Dell platform setting control interface"); 627 + MODULE_LICENSE("GPL");
+6 -8
drivers/platform/x86/i2c-multi-instantiate.c
··· 13 13 #include <linux/kernel.h> 14 14 #include <linux/module.h> 15 15 #include <linux/platform_device.h> 16 + #include <linux/property.h> 16 17 #include <linux/types.h> 17 18 18 19 #define IRQ_RESOURCE_TYPE GENMASK(1, 0) ··· 60 59 static int i2c_multi_inst_probe(struct platform_device *pdev) 61 60 { 62 61 struct i2c_multi_inst_data *multi; 63 - const struct acpi_device_id *match; 64 62 const struct i2c_inst_data *inst_data; 65 63 struct i2c_board_info board_info = {}; 66 64 struct device *dev = &pdev->dev; ··· 67 67 char name[32]; 68 68 int i, ret; 69 69 70 - match = acpi_match_device(dev->driver->acpi_match_table, dev); 71 - if (!match) { 70 + inst_data = device_get_match_data(dev); 71 + if (!inst_data) { 72 72 dev_err(dev, "Error ACPI match data is missing\n"); 73 73 return -ENODEV; 74 74 } 75 - inst_data = (const struct i2c_inst_data *)match->driver_data; 76 75 77 76 adev = ACPI_COMPANION(dev); 78 77 ··· 117 118 } 118 119 multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info); 119 120 if (IS_ERR(multi->clients[i])) { 120 - ret = PTR_ERR(multi->clients[i]); 121 - if (ret != -EPROBE_DEFER) 122 - dev_err(dev, "Error creating i2c-client, idx %d\n", i); 121 + ret = dev_err_probe(dev, PTR_ERR(multi->clients[i]), 122 + "Error creating i2c-client, idx %d\n", i); 123 123 goto error; 124 124 } 125 125 } ··· 187 189 static struct platform_driver i2c_multi_inst_driver = { 188 190 .driver = { 189 191 .name = "I2C multi instantiate pseudo device driver", 190 - .acpi_match_table = ACPI_PTR(i2c_multi_inst_acpi_ids), 192 + .acpi_match_table = i2c_multi_inst_acpi_ids, 191 193 }, 192 194 .probe = i2c_multi_inst_probe, 193 195 .remove = i2c_multi_inst_remove,
+138 -2
drivers/platform/x86/intel-hid.c
··· 15 15 #include <linux/platform_device.h> 16 16 #include <linux/suspend.h> 17 17 18 + /* When NOT in tablet mode, VGBS returns with the flag 0x40 */ 19 + #define TABLET_MODE_FLAG BIT(6) 20 + 18 21 MODULE_LICENSE("GPL"); 19 22 MODULE_AUTHOR("Alex Hung"); 20 23 21 24 static const struct acpi_device_id intel_hid_ids[] = { 22 25 {"INT33D5", 0}, 23 26 {"INTC1051", 0}, 27 + {"INTC1054", 0}, 24 28 {"", 0}, 25 29 }; 26 30 MODULE_DEVICE_TABLE(acpi, intel_hid_ids); ··· 93 89 { } 94 90 }; 95 91 92 + /* 93 + * Some convertible use the intel-hid ACPI interface to report SW_TABLET_MODE, 94 + * these need to be compared via a DMI based authorization list because some 95 + * models have unreliable VGBS return which could cause incorrect 96 + * SW_TABLET_MODE report. 97 + */ 98 + static const struct dmi_system_id dmi_vgbs_allow_list[] = { 99 + { 100 + .matches = { 101 + DMI_MATCH(DMI_SYS_VENDOR, "HP"), 102 + DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Convertible 15-df0xxx"), 103 + }, 104 + }, 105 + { } 106 + }; 107 + 96 108 struct intel_hid_priv { 97 109 struct input_dev *input_dev; 98 110 struct input_dev *array; 111 + struct input_dev *switches; 99 112 bool wakeup_mode; 100 113 }; 101 114 ··· 162 141 163 142 method_name = (char *)intel_hid_dsm_fn_to_method[fn_index]; 164 143 165 - if (!(intel_hid_dsm_fn_mask & fn_index)) 144 + if (!(intel_hid_dsm_fn_mask & BIT(fn_index))) 166 145 goto skip_dsm_exec; 167 146 168 147 /* All methods expects a package with one integer element */ ··· 235 214 obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid, 1, 0, NULL, 236 215 ACPI_TYPE_BUFFER); 237 216 if (obj) { 238 - intel_hid_dsm_fn_mask = *obj->buffer.pointer; 217 + switch (obj->buffer.length) { 218 + default: 219 + case 2: 220 + intel_hid_dsm_fn_mask = *(u16 *)obj->buffer.pointer; 221 + break; 222 + case 1: 223 + intel_hid_dsm_fn_mask = *obj->buffer.pointer; 224 + break; 225 + case 0: 226 + acpi_handle_warn(handle, "intel_hid_dsm_fn_mask length is zero\n"); 227 + intel_hid_dsm_fn_mask = 0; 228 + break; 229 + } 239 230 ACPI_FREE(obj); 240 231 } 241 232 ··· 380 347 return input_register_device(priv->array); 381 348 } 382 349 350 + static int intel_hid_switches_setup(struct platform_device *device) 351 + { 352 + struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); 353 + 354 + /* Setup input device for switches */ 355 + priv->switches = devm_input_allocate_device(&device->dev); 356 + if (!priv->switches) 357 + return -ENOMEM; 358 + 359 + __set_bit(EV_SW, priv->switches->evbit); 360 + __set_bit(SW_TABLET_MODE, priv->switches->swbit); 361 + 362 + priv->switches->name = "Intel HID switches"; 363 + priv->switches->id.bustype = BUS_HOST; 364 + return input_register_device(priv->switches); 365 + } 366 + 367 + static void report_tablet_mode_state(struct platform_device *device) 368 + { 369 + struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); 370 + acpi_handle handle = ACPI_HANDLE(&device->dev); 371 + unsigned long long vgbs; 372 + int m; 373 + 374 + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_VGBS_FN, &vgbs)) 375 + return; 376 + 377 + m = !(vgbs & TABLET_MODE_FLAG); 378 + input_report_switch(priv->switches, SW_TABLET_MODE, m); 379 + input_sync(priv->switches); 380 + } 381 + 382 + static bool report_tablet_mode_event(struct input_dev *input_dev, u32 event) 383 + { 384 + if (!input_dev) 385 + return false; 386 + 387 + switch (event) { 388 + case 0xcc: 389 + input_report_switch(input_dev, SW_TABLET_MODE, 1); 390 + input_sync(input_dev); 391 + return true; 392 + case 0xcd: 393 + input_report_switch(input_dev, SW_TABLET_MODE, 0); 394 + input_sync(input_dev); 395 + return true; 396 + default: 397 + return false; 398 + } 399 + } 400 + 383 401 static void notify_handler(acpi_handle handle, u32 event, void *context) 384 402 { 385 403 struct platform_device *device = context; 386 404 struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); 387 405 unsigned long long ev_index; 406 + int err; 407 + 408 + /* 409 + * Some convertible have unreliable VGBS return which could cause incorrect 410 + * SW_TABLET_MODE report, in these cases we enable support when receiving 411 + * the first event instead of during driver setup. 412 + * 413 + * Some 360 degree hinges (yoga) style 2-in-1 devices use 2 accelerometers 414 + * to allow the OS to determine the angle between the display and the base 415 + * of the device. On Windows these are read by a special HingeAngleService 416 + * process which calls an ACPI DSM (Device Specific Method) on the 417 + * ACPI KIOX010A device node for the sensor in the display, to let the 418 + * firmware know if the 2-in-1 is in tablet- or laptop-mode so that it can 419 + * disable the kbd and touchpad to avoid spurious input in tablet-mode. 420 + * 421 + * The linux kxcjk1013 driver calls the DSM for this once at probe time 422 + * to ensure that the builtin kbd and touchpad work. On some devices this 423 + * causes a "spurious" 0xcd event on the intel-hid ACPI dev. In this case 424 + * there is not a functional tablet-mode switch, so we should not register 425 + * the tablet-mode switch device. 426 + */ 427 + if (!priv->switches && (event == 0xcc || event == 0xcd) && 428 + !acpi_dev_present("KIOX010A", NULL, -1)) { 429 + dev_info(&device->dev, "switch event received, enable switches supports\n"); 430 + err = intel_hid_switches_setup(device); 431 + if (err) 432 + pr_err("Failed to setup Intel HID switches\n"); 433 + } 388 434 389 435 if (priv->wakeup_mode) { 390 436 /* ··· 473 361 * device object on power button actions while suspended. 474 362 */ 475 363 if (event == 0xce) 364 + goto wakeup; 365 + 366 + /* 367 + * Switch events will wake the device and report the new switch 368 + * position to the input subsystem. 369 + */ 370 + if (priv->switches && (event == 0xcc || event == 0xcd)) 476 371 goto wakeup; 477 372 478 373 /* Wake up on 5-button array events only. */ ··· 493 374 494 375 wakeup: 495 376 pm_wakeup_hard_event(&device->dev); 377 + 378 + if (report_tablet_mode_event(priv->switches, event)) 379 + return; 380 + 496 381 return; 497 382 } 498 383 ··· 520 397 return; 521 398 } 522 399 } 400 + 401 + if (report_tablet_mode_event(priv->switches, event)) 402 + return; 523 403 524 404 /* 0xC0 is for HID events, other values are for 5 button array */ 525 405 if (event != 0xc0) { ··· 609 483 err = intel_button_array_input_setup(device); 610 484 if (err) 611 485 pr_err("Failed to setup Intel 5 button array hotkeys\n"); 486 + } 487 + 488 + /* Setup switches for devices that we know VGBS return correctly */ 489 + if (dmi_check_system(dmi_vgbs_allow_list)) { 490 + dev_info(&device->dev, "platform supports switches\n"); 491 + err = intel_hid_switches_setup(device); 492 + if (err) 493 + pr_err("Failed to setup Intel HID switches\n"); 494 + else 495 + report_tablet_mode_state(device); 612 496 } 613 497 614 498 status = acpi_install_notify_handler(handle,
+14 -4
drivers/platform/x86/intel-vbtn.c
··· 15 15 #include <linux/platform_device.h> 16 16 #include <linux/suspend.h> 17 17 18 + /* Returned when NOT in tablet mode on some HP Stream x360 11 models */ 19 + #define VGBS_TABLET_MODE_FLAG_ALT 0x10 18 20 /* When NOT in tablet mode, VGBS returns with the flag 0x40 */ 19 - #define TABLET_MODE_FLAG 0x40 20 - #define DOCK_MODE_FLAG 0x80 21 + #define VGBS_TABLET_MODE_FLAG 0x40 22 + #define VGBS_DOCK_MODE_FLAG 0x80 23 + 24 + #define VGBS_TABLET_MODE_FLAGS (VGBS_TABLET_MODE_FLAG | VGBS_TABLET_MODE_FLAG_ALT) 21 25 22 26 MODULE_LICENSE("GPL"); 23 27 MODULE_AUTHOR("AceLan Kao"); ··· 76 72 if (ACPI_FAILURE(status)) 77 73 return; 78 74 79 - m = !(vgbs & TABLET_MODE_FLAG); 75 + m = !(vgbs & VGBS_TABLET_MODE_FLAGS); 80 76 input_report_switch(priv->input_dev, SW_TABLET_MODE, m); 81 - m = (vgbs & DOCK_MODE_FLAG) ? 1 : 0; 77 + m = (vgbs & VGBS_DOCK_MODE_FLAG) ? 1 : 0; 82 78 input_report_switch(priv->input_dev, SW_DOCK, m); 83 79 } 84 80 ··· 214 210 .matches = { 215 211 DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 216 212 DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion 13 x360 PC"), 213 + }, 214 + }, 215 + { 216 + .matches = { 217 + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 218 + DMI_MATCH(DMI_PRODUCT_NAME, "Switch SA5-271"), 217 219 }, 218 220 }, 219 221 {} /* Array terminator */
+1 -1
drivers/platform/x86/intel_pmc_core.c
··· 929 929 fd |= CNP_PMC_LATCH_SLPS0_EVENTS; 930 930 pmc_core_reg_write(pmcdev, map->slps0_dbg_offset, fd); 931 931 932 - slps0_dbg_latch = 0; 932 + slps0_dbg_latch = false; 933 933 934 934 out_unlock: 935 935 mutex_unlock(&pmcdev->lock);
+298
drivers/platform/x86/intel_pmt_class.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitory Technology Telemetry driver 4 + * 5 + * Copyright (c) 2020, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/mm.h> 14 + #include <linux/pci.h> 15 + 16 + #include "intel_pmt_class.h" 17 + 18 + #define PMT_XA_START 0 19 + #define PMT_XA_MAX INT_MAX 20 + #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) 21 + 22 + /* 23 + * sysfs 24 + */ 25 + static ssize_t 26 + intel_pmt_read(struct file *filp, struct kobject *kobj, 27 + struct bin_attribute *attr, char *buf, loff_t off, 28 + size_t count) 29 + { 30 + struct intel_pmt_entry *entry = container_of(attr, 31 + struct intel_pmt_entry, 32 + pmt_bin_attr); 33 + 34 + if (off < 0) 35 + return -EINVAL; 36 + 37 + if (off >= entry->size) 38 + return 0; 39 + 40 + if (count > entry->size - off) 41 + count = entry->size - off; 42 + 43 + memcpy_fromio(buf, entry->base + off, count); 44 + 45 + return count; 46 + } 47 + 48 + static int 49 + intel_pmt_mmap(struct file *filp, struct kobject *kobj, 50 + struct bin_attribute *attr, struct vm_area_struct *vma) 51 + { 52 + struct intel_pmt_entry *entry = container_of(attr, 53 + struct intel_pmt_entry, 54 + pmt_bin_attr); 55 + unsigned long vsize = vma->vm_end - vma->vm_start; 56 + struct device *dev = kobj_to_dev(kobj); 57 + unsigned long phys = entry->base_addr; 58 + unsigned long pfn = PFN_DOWN(phys); 59 + unsigned long psize; 60 + 61 + if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) 62 + return -EROFS; 63 + 64 + psize = (PFN_UP(entry->base_addr + entry->size) - pfn) * PAGE_SIZE; 65 + if (vsize > psize) { 66 + dev_err(dev, "Requested mmap size is too large\n"); 67 + return -EINVAL; 68 + } 69 + 70 + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 71 + if (io_remap_pfn_range(vma, vma->vm_start, pfn, 72 + vsize, vma->vm_page_prot)) 73 + return -EAGAIN; 74 + 75 + return 0; 76 + } 77 + 78 + static ssize_t 79 + guid_show(struct device *dev, struct device_attribute *attr, char *buf) 80 + { 81 + struct intel_pmt_entry *entry = dev_get_drvdata(dev); 82 + 83 + return sprintf(buf, "0x%x\n", entry->guid); 84 + } 85 + static DEVICE_ATTR_RO(guid); 86 + 87 + static ssize_t size_show(struct device *dev, struct device_attribute *attr, 88 + char *buf) 89 + { 90 + struct intel_pmt_entry *entry = dev_get_drvdata(dev); 91 + 92 + return sprintf(buf, "%zu\n", entry->size); 93 + } 94 + static DEVICE_ATTR_RO(size); 95 + 96 + static ssize_t 97 + offset_show(struct device *dev, struct device_attribute *attr, char *buf) 98 + { 99 + struct intel_pmt_entry *entry = dev_get_drvdata(dev); 100 + 101 + return sprintf(buf, "%lu\n", offset_in_page(entry->base_addr)); 102 + } 103 + static DEVICE_ATTR_RO(offset); 104 + 105 + static struct attribute *intel_pmt_attrs[] = { 106 + &dev_attr_guid.attr, 107 + &dev_attr_size.attr, 108 + &dev_attr_offset.attr, 109 + NULL 110 + }; 111 + ATTRIBUTE_GROUPS(intel_pmt); 112 + 113 + static struct class intel_pmt_class = { 114 + .name = "intel_pmt", 115 + .owner = THIS_MODULE, 116 + .dev_groups = intel_pmt_groups, 117 + }; 118 + 119 + static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, 120 + struct intel_pmt_header *header, 121 + struct device *dev, 122 + struct resource *disc_res) 123 + { 124 + struct pci_dev *pci_dev = to_pci_dev(dev->parent); 125 + u8 bir; 126 + 127 + /* 128 + * The base offset should always be 8 byte aligned. 129 + * 130 + * For non-local access types the lower 3 bits of base offset 131 + * contains the index of the base address register where the 132 + * telemetry can be found. 133 + */ 134 + bir = GET_BIR(header->base_offset); 135 + 136 + /* Local access and BARID only for now */ 137 + switch (header->access_type) { 138 + case ACCESS_LOCAL: 139 + if (bir) { 140 + dev_err(dev, 141 + "Unsupported BAR index %d for access type %d\n", 142 + bir, header->access_type); 143 + return -EINVAL; 144 + } 145 + /* 146 + * For access_type LOCAL, the base address is as follows: 147 + * base address = end of discovery region + base offset 148 + */ 149 + entry->base_addr = disc_res->end + 1 + header->base_offset; 150 + break; 151 + case ACCESS_BARID: 152 + /* 153 + * If another BAR was specified then the base offset 154 + * represents the offset within that BAR. SO retrieve the 155 + * address from the parent PCI device and add offset. 156 + */ 157 + entry->base_addr = pci_resource_start(pci_dev, bir) + 158 + GET_ADDRESS(header->base_offset); 159 + break; 160 + default: 161 + dev_err(dev, "Unsupported access type %d\n", 162 + header->access_type); 163 + return -EINVAL; 164 + } 165 + 166 + entry->guid = header->guid; 167 + entry->size = header->size; 168 + 169 + return 0; 170 + } 171 + 172 + static int intel_pmt_dev_register(struct intel_pmt_entry *entry, 173 + struct intel_pmt_namespace *ns, 174 + struct device *parent) 175 + { 176 + struct resource res; 177 + struct device *dev; 178 + int ret; 179 + 180 + ret = xa_alloc(ns->xa, &entry->devid, entry, PMT_XA_LIMIT, GFP_KERNEL); 181 + if (ret) 182 + return ret; 183 + 184 + dev = device_create(&intel_pmt_class, parent, MKDEV(0, 0), entry, 185 + "%s%d", ns->name, entry->devid); 186 + 187 + if (IS_ERR(dev)) { 188 + dev_err(parent, "Could not create %s%d device node\n", 189 + ns->name, entry->devid); 190 + ret = PTR_ERR(dev); 191 + goto fail_dev_create; 192 + } 193 + 194 + entry->kobj = &dev->kobj; 195 + 196 + if (ns->attr_grp) { 197 + ret = sysfs_create_group(entry->kobj, ns->attr_grp); 198 + if (ret) 199 + goto fail_sysfs; 200 + } 201 + 202 + /* if size is 0 assume no data buffer, so no file needed */ 203 + if (!entry->size) 204 + return 0; 205 + 206 + res.start = entry->base_addr; 207 + res.end = res.start + entry->size - 1; 208 + res.flags = IORESOURCE_MEM; 209 + 210 + entry->base = devm_ioremap_resource(dev, &res); 211 + if (IS_ERR(entry->base)) { 212 + ret = PTR_ERR(entry->base); 213 + goto fail_ioremap; 214 + } 215 + 216 + sysfs_bin_attr_init(&entry->pmt_bin_attr); 217 + entry->pmt_bin_attr.attr.name = ns->name; 218 + entry->pmt_bin_attr.attr.mode = 0440; 219 + entry->pmt_bin_attr.mmap = intel_pmt_mmap; 220 + entry->pmt_bin_attr.read = intel_pmt_read; 221 + entry->pmt_bin_attr.size = entry->size; 222 + 223 + ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); 224 + if (!ret) 225 + return 0; 226 + 227 + fail_ioremap: 228 + if (ns->attr_grp) 229 + sysfs_remove_group(entry->kobj, ns->attr_grp); 230 + fail_sysfs: 231 + device_unregister(dev); 232 + fail_dev_create: 233 + xa_erase(ns->xa, entry->devid); 234 + 235 + return ret; 236 + } 237 + 238 + int intel_pmt_dev_create(struct intel_pmt_entry *entry, 239 + struct intel_pmt_namespace *ns, 240 + struct platform_device *pdev, int idx) 241 + { 242 + struct intel_pmt_header header; 243 + struct resource *disc_res; 244 + int ret = -ENODEV; 245 + 246 + disc_res = platform_get_resource(pdev, IORESOURCE_MEM, idx); 247 + if (!disc_res) 248 + return ret; 249 + 250 + entry->disc_table = devm_platform_ioremap_resource(pdev, idx); 251 + if (IS_ERR(entry->disc_table)) 252 + return PTR_ERR(entry->disc_table); 253 + 254 + ret = ns->pmt_header_decode(entry, &header, &pdev->dev); 255 + if (ret) 256 + return ret; 257 + 258 + ret = intel_pmt_populate_entry(entry, &header, &pdev->dev, disc_res); 259 + if (ret) 260 + return ret; 261 + 262 + return intel_pmt_dev_register(entry, ns, &pdev->dev); 263 + 264 + } 265 + EXPORT_SYMBOL_GPL(intel_pmt_dev_create); 266 + 267 + void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, 268 + struct intel_pmt_namespace *ns) 269 + { 270 + struct device *dev = kobj_to_dev(entry->kobj); 271 + 272 + if (entry->size) 273 + sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); 274 + 275 + if (ns->attr_grp) 276 + sysfs_remove_group(entry->kobj, ns->attr_grp); 277 + 278 + device_unregister(dev); 279 + xa_erase(ns->xa, entry->devid); 280 + } 281 + EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy); 282 + 283 + static int __init pmt_class_init(void) 284 + { 285 + return class_register(&intel_pmt_class); 286 + } 287 + 288 + static void __exit pmt_class_exit(void) 289 + { 290 + class_unregister(&intel_pmt_class); 291 + } 292 + 293 + module_init(pmt_class_init); 294 + module_exit(pmt_class_exit); 295 + 296 + MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); 297 + MODULE_DESCRIPTION("Intel PMT Class driver"); 298 + MODULE_LICENSE("GPL v2");
+52
drivers/platform/x86/intel_pmt_class.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _INTEL_PMT_CLASS_H 3 + #define _INTEL_PMT_CLASS_H 4 + 5 + #include <linux/platform_device.h> 6 + #include <linux/xarray.h> 7 + #include <linux/types.h> 8 + #include <linux/bits.h> 9 + #include <linux/err.h> 10 + #include <linux/io.h> 11 + 12 + /* PMT access types */ 13 + #define ACCESS_BARID 2 14 + #define ACCESS_LOCAL 3 15 + 16 + /* PMT discovery base address/offset register layout */ 17 + #define GET_BIR(v) ((v) & GENMASK(2, 0)) 18 + #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) 19 + 20 + struct intel_pmt_entry { 21 + struct bin_attribute pmt_bin_attr; 22 + struct kobject *kobj; 23 + void __iomem *disc_table; 24 + void __iomem *base; 25 + unsigned long base_addr; 26 + size_t size; 27 + u32 guid; 28 + int devid; 29 + }; 30 + 31 + struct intel_pmt_header { 32 + u32 base_offset; 33 + u32 size; 34 + u32 guid; 35 + u8 access_type; 36 + }; 37 + 38 + struct intel_pmt_namespace { 39 + const char *name; 40 + struct xarray *xa; 41 + const struct attribute_group *attr_grp; 42 + int (*pmt_header_decode)(struct intel_pmt_entry *entry, 43 + struct intel_pmt_header *header, 44 + struct device *dev); 45 + }; 46 + 47 + int intel_pmt_dev_create(struct intel_pmt_entry *entry, 48 + struct intel_pmt_namespace *ns, 49 + struct platform_device *pdev, int idx); 50 + void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, 51 + struct intel_pmt_namespace *ns); 52 + #endif
+328
drivers/platform/x86/intel_pmt_crashlog.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitoring Technology Crashlog driver 4 + * 5 + * Copyright (c) 2020, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/pci.h> 14 + #include <linux/slab.h> 15 + #include <linux/uaccess.h> 16 + #include <linux/overflow.h> 17 + 18 + #include "intel_pmt_class.h" 19 + 20 + #define DRV_NAME "pmt_crashlog" 21 + 22 + /* Crashlog discovery header types */ 23 + #define CRASH_TYPE_OOBMSM 1 24 + 25 + /* Control Flags */ 26 + #define CRASHLOG_FLAG_DISABLE BIT(27) 27 + 28 + /* 29 + * Bits 28 and 29 control the state of bit 31. 30 + * 31 + * Bit 28 will clear bit 31, if set, allowing a new crashlog to be captured. 32 + * Bit 29 will immediately trigger a crashlog to be generated, setting bit 31. 33 + * Bit 30 is read-only and reserved as 0. 34 + * Bit 31 is the read-only status with a 1 indicating log is complete. 35 + */ 36 + #define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(28) 37 + #define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(29) 38 + #define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31) 39 + #define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28) 40 + 41 + /* Crashlog Discovery Header */ 42 + #define CONTROL_OFFSET 0x0 43 + #define GUID_OFFSET 0x4 44 + #define BASE_OFFSET 0x8 45 + #define SIZE_OFFSET 0xC 46 + #define GET_ACCESS(v) ((v) & GENMASK(3, 0)) 47 + #define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) 48 + #define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16) 49 + /* size is in bytes */ 50 + #define GET_SIZE(v) ((v) * sizeof(u32)) 51 + 52 + struct crashlog_entry { 53 + /* entry must be first member of struct */ 54 + struct intel_pmt_entry entry; 55 + struct mutex control_mutex; 56 + }; 57 + 58 + struct pmt_crashlog_priv { 59 + int num_entries; 60 + struct crashlog_entry entry[]; 61 + }; 62 + 63 + /* 64 + * I/O 65 + */ 66 + static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) 67 + { 68 + u32 control = readl(entry->disc_table + CONTROL_OFFSET); 69 + 70 + /* return current value of the crashlog complete flag */ 71 + return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE); 72 + } 73 + 74 + static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry) 75 + { 76 + u32 control = readl(entry->disc_table + CONTROL_OFFSET); 77 + 78 + /* return current value of the crashlog disabled flag */ 79 + return !!(control & CRASHLOG_FLAG_DISABLE); 80 + } 81 + 82 + static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) 83 + { 84 + u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); 85 + u32 crash_type, version; 86 + 87 + crash_type = GET_TYPE(discovery_header); 88 + version = GET_VERSION(discovery_header); 89 + 90 + /* 91 + * Currently we only recognize OOBMSM version 0 devices. 92 + * We can ignore all other crashlog devices in the system. 93 + */ 94 + return crash_type == CRASH_TYPE_OOBMSM && version == 0; 95 + } 96 + 97 + static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry, 98 + bool disable) 99 + { 100 + u32 control = readl(entry->disc_table + CONTROL_OFFSET); 101 + 102 + /* clear trigger bits so we are only modifying disable flag */ 103 + control &= ~CRASHLOG_FLAG_TRIGGER_MASK; 104 + 105 + if (disable) 106 + control |= CRASHLOG_FLAG_DISABLE; 107 + else 108 + control &= ~CRASHLOG_FLAG_DISABLE; 109 + 110 + writel(control, entry->disc_table + CONTROL_OFFSET); 111 + } 112 + 113 + static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry) 114 + { 115 + u32 control = readl(entry->disc_table + CONTROL_OFFSET); 116 + 117 + control &= ~CRASHLOG_FLAG_TRIGGER_MASK; 118 + control |= CRASHLOG_FLAG_TRIGGER_CLEAR; 119 + 120 + writel(control, entry->disc_table + CONTROL_OFFSET); 121 + } 122 + 123 + static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) 124 + { 125 + u32 control = readl(entry->disc_table + CONTROL_OFFSET); 126 + 127 + control &= ~CRASHLOG_FLAG_TRIGGER_MASK; 128 + control |= CRASHLOG_FLAG_TRIGGER_EXECUTE; 129 + 130 + writel(control, entry->disc_table + CONTROL_OFFSET); 131 + } 132 + 133 + /* 134 + * sysfs 135 + */ 136 + static ssize_t 137 + enable_show(struct device *dev, struct device_attribute *attr, char *buf) 138 + { 139 + struct intel_pmt_entry *entry = dev_get_drvdata(dev); 140 + int enabled = !pmt_crashlog_disabled(entry); 141 + 142 + return sprintf(buf, "%d\n", enabled); 143 + } 144 + 145 + static ssize_t 146 + enable_store(struct device *dev, struct device_attribute *attr, 147 + const char *buf, size_t count) 148 + { 149 + struct crashlog_entry *entry; 150 + bool enabled; 151 + int result; 152 + 153 + entry = dev_get_drvdata(dev); 154 + 155 + result = kstrtobool(buf, &enabled); 156 + if (result) 157 + return result; 158 + 159 + mutex_lock(&entry->control_mutex); 160 + pmt_crashlog_set_disable(&entry->entry, !enabled); 161 + mutex_unlock(&entry->control_mutex); 162 + 163 + return count; 164 + } 165 + static DEVICE_ATTR_RW(enable); 166 + 167 + static ssize_t 168 + trigger_show(struct device *dev, struct device_attribute *attr, char *buf) 169 + { 170 + struct intel_pmt_entry *entry; 171 + int trigger; 172 + 173 + entry = dev_get_drvdata(dev); 174 + trigger = pmt_crashlog_complete(entry); 175 + 176 + return sprintf(buf, "%d\n", trigger); 177 + } 178 + 179 + static ssize_t 180 + trigger_store(struct device *dev, struct device_attribute *attr, 181 + const char *buf, size_t count) 182 + { 183 + struct crashlog_entry *entry; 184 + bool trigger; 185 + int result; 186 + 187 + entry = dev_get_drvdata(dev); 188 + 189 + result = kstrtobool(buf, &trigger); 190 + if (result) 191 + return result; 192 + 193 + mutex_lock(&entry->control_mutex); 194 + 195 + if (!trigger) { 196 + pmt_crashlog_set_clear(&entry->entry); 197 + } else if (pmt_crashlog_complete(&entry->entry)) { 198 + /* we cannot trigger a new crash if one is still pending */ 199 + result = -EEXIST; 200 + goto err; 201 + } else if (pmt_crashlog_disabled(&entry->entry)) { 202 + /* if device is currently disabled, return busy */ 203 + result = -EBUSY; 204 + goto err; 205 + } else { 206 + pmt_crashlog_set_execute(&entry->entry); 207 + } 208 + 209 + result = count; 210 + err: 211 + mutex_unlock(&entry->control_mutex); 212 + return result; 213 + } 214 + static DEVICE_ATTR_RW(trigger); 215 + 216 + static struct attribute *pmt_crashlog_attrs[] = { 217 + &dev_attr_enable.attr, 218 + &dev_attr_trigger.attr, 219 + NULL 220 + }; 221 + 222 + static struct attribute_group pmt_crashlog_group = { 223 + .attrs = pmt_crashlog_attrs, 224 + }; 225 + 226 + static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, 227 + struct intel_pmt_header *header, 228 + struct device *dev) 229 + { 230 + void __iomem *disc_table = entry->disc_table; 231 + struct crashlog_entry *crashlog; 232 + 233 + if (!pmt_crashlog_supported(entry)) 234 + return 1; 235 + 236 + /* initialize control mutex */ 237 + crashlog = container_of(entry, struct crashlog_entry, entry); 238 + mutex_init(&crashlog->control_mutex); 239 + 240 + header->access_type = GET_ACCESS(readl(disc_table)); 241 + header->guid = readl(disc_table + GUID_OFFSET); 242 + header->base_offset = readl(disc_table + BASE_OFFSET); 243 + 244 + /* Size is measured in DWORDS, but accessor returns bytes */ 245 + header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); 246 + 247 + return 0; 248 + } 249 + 250 + static DEFINE_XARRAY_ALLOC(crashlog_array); 251 + static struct intel_pmt_namespace pmt_crashlog_ns = { 252 + .name = "crashlog", 253 + .xa = &crashlog_array, 254 + .attr_grp = &pmt_crashlog_group, 255 + .pmt_header_decode = pmt_crashlog_header_decode, 256 + }; 257 + 258 + /* 259 + * initialization 260 + */ 261 + static int pmt_crashlog_remove(struct platform_device *pdev) 262 + { 263 + struct pmt_crashlog_priv *priv = platform_get_drvdata(pdev); 264 + int i; 265 + 266 + for (i = 0; i < priv->num_entries; i++) 267 + intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); 268 + 269 + return 0; 270 + } 271 + 272 + static int pmt_crashlog_probe(struct platform_device *pdev) 273 + { 274 + struct pmt_crashlog_priv *priv; 275 + size_t size; 276 + int i, ret; 277 + 278 + size = struct_size(priv, entry, pdev->num_resources); 279 + priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); 280 + if (!priv) 281 + return -ENOMEM; 282 + 283 + platform_set_drvdata(pdev, priv); 284 + 285 + for (i = 0; i < pdev->num_resources; i++) { 286 + struct intel_pmt_entry *entry = &priv->entry[i].entry; 287 + 288 + ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, pdev, i); 289 + if (ret < 0) 290 + goto abort_probe; 291 + if (ret) 292 + continue; 293 + 294 + priv->num_entries++; 295 + } 296 + 297 + return 0; 298 + abort_probe: 299 + pmt_crashlog_remove(pdev); 300 + return ret; 301 + } 302 + 303 + static struct platform_driver pmt_crashlog_driver = { 304 + .driver = { 305 + .name = DRV_NAME, 306 + }, 307 + .remove = pmt_crashlog_remove, 308 + .probe = pmt_crashlog_probe, 309 + }; 310 + 311 + static int __init pmt_crashlog_init(void) 312 + { 313 + return platform_driver_register(&pmt_crashlog_driver); 314 + } 315 + 316 + static void __exit pmt_crashlog_exit(void) 317 + { 318 + platform_driver_unregister(&pmt_crashlog_driver); 319 + xa_destroy(&crashlog_array); 320 + } 321 + 322 + module_init(pmt_crashlog_init); 323 + module_exit(pmt_crashlog_exit); 324 + 325 + MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); 326 + MODULE_DESCRIPTION("Intel PMT Crashlog driver"); 327 + MODULE_ALIAS("platform:" DRV_NAME); 328 + MODULE_LICENSE("GPL v2");
+160
drivers/platform/x86/intel_pmt_telemetry.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitory Technology Telemetry driver 4 + * 5 + * Copyright (c) 2020, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + * Author: "David E. Box" <david.e.box@linux.intel.com> 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/pci.h> 14 + #include <linux/slab.h> 15 + #include <linux/uaccess.h> 16 + #include <linux/overflow.h> 17 + 18 + #include "intel_pmt_class.h" 19 + 20 + #define TELEM_DEV_NAME "pmt_telemetry" 21 + 22 + #define TELEM_SIZE_OFFSET 0x0 23 + #define TELEM_GUID_OFFSET 0x4 24 + #define TELEM_BASE_OFFSET 0x8 25 + #define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) 26 + /* size is in bytes */ 27 + #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10) 28 + 29 + /* Used by client hardware to identify a fixed telemetry entry*/ 30 + #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 31 + 32 + struct pmt_telem_priv { 33 + int num_entries; 34 + struct intel_pmt_entry entry[]; 35 + }; 36 + 37 + /* 38 + * Early implementations of PMT on client platforms have some 39 + * differences from the server platforms (which use the Out Of Band 40 + * Management Services Module OOBMSM). This list tracks those 41 + * platforms as needed to handle those differences. Newer client 42 + * platforms are expected to be fully compatible with server. 43 + */ 44 + static const struct pci_device_id pmt_telem_early_client_pci_ids[] = { 45 + { PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */ 46 + { PCI_VDEVICE(INTEL, 0x467d) }, /* ADL */ 47 + { } 48 + }; 49 + 50 + static bool intel_pmt_is_early_client_hw(struct device *dev) 51 + { 52 + struct pci_dev *parent = to_pci_dev(dev->parent); 53 + 54 + return !!pci_match_id(pmt_telem_early_client_pci_ids, parent); 55 + } 56 + 57 + static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, 58 + struct device *dev) 59 + { 60 + u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); 61 + 62 + if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID) 63 + return false; 64 + 65 + return intel_pmt_is_early_client_hw(dev); 66 + } 67 + 68 + static int pmt_telem_header_decode(struct intel_pmt_entry *entry, 69 + struct intel_pmt_header *header, 70 + struct device *dev) 71 + { 72 + void __iomem *disc_table = entry->disc_table; 73 + 74 + if (pmt_telem_region_overlaps(entry, dev)) 75 + return 1; 76 + 77 + header->access_type = TELEM_ACCESS(readl(disc_table)); 78 + header->guid = readl(disc_table + TELEM_GUID_OFFSET); 79 + header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); 80 + 81 + /* Size is measured in DWORDS, but accessor returns bytes */ 82 + header->size = TELEM_SIZE(readl(disc_table)); 83 + 84 + return 0; 85 + } 86 + 87 + static DEFINE_XARRAY_ALLOC(telem_array); 88 + static struct intel_pmt_namespace pmt_telem_ns = { 89 + .name = "telem", 90 + .xa = &telem_array, 91 + .pmt_header_decode = pmt_telem_header_decode, 92 + }; 93 + 94 + static int pmt_telem_remove(struct platform_device *pdev) 95 + { 96 + struct pmt_telem_priv *priv = platform_get_drvdata(pdev); 97 + int i; 98 + 99 + for (i = 0; i < priv->num_entries; i++) 100 + intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); 101 + 102 + return 0; 103 + } 104 + 105 + static int pmt_telem_probe(struct platform_device *pdev) 106 + { 107 + struct pmt_telem_priv *priv; 108 + size_t size; 109 + int i, ret; 110 + 111 + size = struct_size(priv, entry, pdev->num_resources); 112 + priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); 113 + if (!priv) 114 + return -ENOMEM; 115 + 116 + platform_set_drvdata(pdev, priv); 117 + 118 + for (i = 0; i < pdev->num_resources; i++) { 119 + struct intel_pmt_entry *entry = &priv->entry[i]; 120 + 121 + ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i); 122 + if (ret < 0) 123 + goto abort_probe; 124 + if (ret) 125 + continue; 126 + 127 + priv->num_entries++; 128 + } 129 + 130 + return 0; 131 + abort_probe: 132 + pmt_telem_remove(pdev); 133 + return ret; 134 + } 135 + 136 + static struct platform_driver pmt_telem_driver = { 137 + .driver = { 138 + .name = TELEM_DEV_NAME, 139 + }, 140 + .remove = pmt_telem_remove, 141 + .probe = pmt_telem_probe, 142 + }; 143 + 144 + static int __init pmt_telem_init(void) 145 + { 146 + return platform_driver_register(&pmt_telem_driver); 147 + } 148 + module_init(pmt_telem_init); 149 + 150 + static void __exit pmt_telem_exit(void) 151 + { 152 + platform_driver_unregister(&pmt_telem_driver); 153 + xa_destroy(&telem_array); 154 + } 155 + module_exit(pmt_telem_exit); 156 + 157 + MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 158 + MODULE_DESCRIPTION("Intel PMT Telemetry driver"); 159 + MODULE_ALIAS("platform:" TELEM_DEV_NAME); 160 + MODULE_LICENSE("GPL v2");
+4 -4
drivers/platform/x86/intel_speed_select_if/isst_if_common.h
··· 10 10 #ifndef __ISST_IF_COMMON_H 11 11 #define __ISST_IF_COMMON_H 12 12 13 - #define INTEL_RAPL_PRIO_DEVID_0 0x3451 14 - #define INTEL_CFG_MBOX_DEVID_0 0x3459 13 + #define PCI_DEVICE_ID_INTEL_RAPL_PRIO_DEVID_0 0x3451 14 + #define PCI_DEVICE_ID_INTEL_CFG_MBOX_DEVID_0 0x3459 15 15 16 - #define INTEL_RAPL_PRIO_DEVID_1 0x3251 17 - #define INTEL_CFG_MBOX_DEVID_1 0x3259 16 + #define PCI_DEVICE_ID_INTEL_RAPL_PRIO_DEVID_1 0x3251 17 + #define PCI_DEVICE_ID_INTEL_CFG_MBOX_DEVID_1 0x3259 18 18 19 19 /* 20 20 * Validate maximum commands in a single request.
+2 -2
drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
··· 146 146 } 147 147 148 148 static const struct pci_device_id isst_if_mbox_ids[] = { 149 - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)}, 150 - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_1)}, 149 + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CFG_MBOX_DEVID_0)}, 150 + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CFG_MBOX_DEVID_1)}, 151 151 { 0 }, 152 152 }; 153 153 MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
+36 -13
drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
··· 20 20 int end; 21 21 }; 22 22 23 - struct isst_mmio_range mmio_range[] = { 23 + static struct isst_mmio_range mmio_range_devid_0[] = { 24 24 {0x04, 0x14}, 25 25 {0x20, 0xD0}, 26 + }; 27 + 28 + static struct isst_mmio_range mmio_range_devid_1[] = { 29 + {0x04, 0x14}, 30 + {0x20, 0x11C}, 26 31 }; 27 32 28 33 struct isst_if_device { 29 34 void __iomem *punit_mmio; 30 35 u32 range_0[5]; 31 - u32 range_1[45]; 36 + u32 range_1[64]; 37 + struct isst_mmio_range *mmio_range; 32 38 struct mutex mutex; 33 39 }; 34 40 ··· 45 39 struct pci_dev *pdev; 46 40 47 41 io_reg = (struct isst_if_io_reg *)cmd_ptr; 48 - if (io_reg->reg < 0x04 || io_reg->reg > 0xD0) 42 + 43 + if (io_reg->reg % 4) 49 44 return -EINVAL; 50 45 51 46 if (io_reg->read_write && !capable(CAP_SYS_ADMIN)) ··· 58 51 59 52 punit_dev = pci_get_drvdata(pdev); 60 53 if (!punit_dev) 54 + return -EINVAL; 55 + 56 + if (io_reg->reg < punit_dev->mmio_range[0].beg || 57 + io_reg->reg > punit_dev->mmio_range[1].end) 61 58 return -EINVAL; 62 59 63 60 /* ··· 82 71 } 83 72 84 73 static const struct pci_device_id isst_if_ids[] = { 85 - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_RAPL_PRIO_DEVID_0)}, 86 - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_RAPL_PRIO_DEVID_1)}, 74 + { PCI_DEVICE_DATA(INTEL, RAPL_PRIO_DEVID_0, &mmio_range_devid_0)}, 75 + { PCI_DEVICE_DATA(INTEL, RAPL_PRIO_DEVID_1, &mmio_range_devid_1)}, 87 76 { 0 }, 88 77 }; 89 78 MODULE_DEVICE_TABLE(pci, isst_if_ids); ··· 120 109 121 110 mutex_init(&punit_dev->mutex); 122 111 pci_set_drvdata(pdev, punit_dev); 112 + punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data; 123 113 124 114 memset(&cb, 0, sizeof(cb)); 125 115 cb.cmd_size = sizeof(struct isst_if_io_reg); ··· 150 138 151 139 for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) 152 140 punit_dev->range_0[i] = readl(punit_dev->punit_mmio + 153 - mmio_range[0].beg + 4 * i); 154 - for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i) 155 - punit_dev->range_1[i] = readl(punit_dev->punit_mmio + 156 - mmio_range[1].beg + 4 * i); 141 + punit_dev->mmio_range[0].beg + 4 * i); 142 + for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i) { 143 + u32 addr; 144 + 145 + addr = punit_dev->mmio_range[1].beg + 4 * i; 146 + if (addr > punit_dev->mmio_range[1].end) 147 + break; 148 + punit_dev->range_1[i] = readl(punit_dev->punit_mmio + addr); 149 + } 157 150 158 151 return 0; 159 152 } ··· 170 153 171 154 for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) 172 155 writel(punit_dev->range_0[i], punit_dev->punit_mmio + 173 - mmio_range[0].beg + 4 * i); 174 - for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i) 175 - writel(punit_dev->range_1[i], punit_dev->punit_mmio + 176 - mmio_range[1].beg + 4 * i); 156 + punit_dev->mmio_range[0].beg + 4 * i); 157 + for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i) { 158 + u32 addr; 159 + 160 + addr = punit_dev->mmio_range[1].beg + 4 * i; 161 + if (addr > punit_dev->mmio_range[1].end) 162 + break; 163 + 164 + writel(punit_dev->range_1[i], punit_dev->punit_mmio + addr); 165 + } 177 166 178 167 return 0; 179 168 }
+10 -23
drivers/platform/x86/mlx-platform.c
··· 319 319 }; 320 320 321 321 /* Platform hotplug devices */ 322 - static struct i2c_board_info mlxplat_mlxcpld_psu[] = { 323 - { 324 - I2C_BOARD_INFO("24c02", 0x51), 325 - }, 326 - { 327 - I2C_BOARD_INFO("24c02", 0x50), 328 - }, 329 - }; 330 - 331 322 static struct i2c_board_info mlxplat_mlxcpld_pwr[] = { 332 323 { 333 324 I2C_BOARD_INFO("dps460", 0x59), ··· 374 383 .label = "psu1", 375 384 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, 376 385 .mask = BIT(0), 377 - .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0], 378 - .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, 386 + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, 379 387 }, 380 388 { 381 389 .label = "psu2", 382 390 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, 383 391 .mask = BIT(1), 384 - .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1], 385 - .hpdev.nr = MLXPLAT_CPLD_PSU_DEFAULT_NR, 392 + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, 386 393 }, 387 394 }; 388 395 ··· 447 458 .aggr_mask = MLXPLAT_CPLD_AGGR_PSU_MASK_DEF, 448 459 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, 449 460 .mask = MLXPLAT_CPLD_PSU_MASK, 450 - .count = ARRAY_SIZE(mlxplat_mlxcpld_psu), 461 + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_psu_items_data), 451 462 .inversed = 1, 452 463 .health = false, 453 464 }, ··· 456 467 .aggr_mask = MLXPLAT_CPLD_AGGR_PWR_MASK_DEF, 457 468 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, 458 469 .mask = MLXPLAT_CPLD_PWR_MASK, 459 - .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), 470 + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_pwr_items_data), 460 471 .inversed = 0, 461 472 .health = false, 462 473 }, ··· 465 476 .aggr_mask = MLXPLAT_CPLD_AGGR_FAN_MASK_DEF, 466 477 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, 467 478 .mask = MLXPLAT_CPLD_FAN_MASK, 468 - .count = ARRAY_SIZE(mlxplat_mlxcpld_fan), 479 + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_fan_items_data), 469 480 .inversed = 1, 470 481 .health = false, 471 482 }, ··· 486 497 .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_CARRIER, 487 498 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, 488 499 .mask = MLXPLAT_CPLD_PSU_MASK, 489 - .count = ARRAY_SIZE(mlxplat_mlxcpld_psu), 500 + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_psu_items_data), 490 501 .inversed = 1, 491 502 .health = false, 492 503 }, ··· 495 506 .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_CARRIER, 496 507 .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, 497 508 .mask = MLXPLAT_CPLD_PWR_MASK, 498 - .count = ARRAY_SIZE(mlxplat_mlxcpld_pwr), 509 + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_pwr_items_data), 499 510 .inversed = 0, 500 511 .health = false, 501 512 }, ··· 504 515 .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_CARRIER, 505 516 .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, 506 517 .mask = MLXPLAT_CPLD_FAN_MASK, 507 - .count = ARRAY_SIZE(mlxplat_mlxcpld_fan), 518 + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_fan_items_data), 508 519 .inversed = 1, 509 520 .health = false, 510 521 }, ··· 592 603 .label = "psu1", 593 604 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, 594 605 .mask = BIT(0), 595 - .hpdev.brdinfo = &mlxplat_mlxcpld_psu[0], 596 - .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, 606 + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, 597 607 }, 598 608 { 599 609 .label = "psu2", 600 610 .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, 601 611 .mask = BIT(1), 602 - .hpdev.brdinfo = &mlxplat_mlxcpld_psu[1], 603 - .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, 612 + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, 604 613 }, 605 614 }; 606 615
+454 -55
drivers/platform/x86/panasonic-laptop.c
··· 12 12 *--------------------------------------------------------------------------- 13 13 * 14 14 * ChangeLog: 15 + * Aug.18, 2020 Kenneth Chan <kenneth.t.chan@gmail.com> 16 + * -v0.98 add platform devices for firmware brightness registers 17 + * add support for battery charging threshold (eco mode) 18 + * resolve hotkey double trigger 19 + * add write support to mute 20 + * fix sticky_key init bug 21 + * fix naming of platform files for consistency with other 22 + * modules 23 + * split MODULE_AUTHOR() by one author per macro call 24 + * replace ACPI prints with pr_*() macros 25 + * -v0.97 add support for cdpower hardware switch 26 + * -v0.96 merge Lucina's enhancement 27 + * Jan.13, 2009 Martin Lucina <mato@kotelna.sk> 28 + * - add support for optical driver power in 29 + * Y and W series 30 + * 15 31 * Sep.23, 2008 Harald Welte <laforge@gnumonks.org> 16 32 * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to 17 33 * drivers/misc/panasonic-laptop.c ··· 131 115 #include <linux/acpi.h> 132 116 #include <linux/input.h> 133 117 #include <linux/input/sparse-keymap.h> 118 + #include <linux/platform_device.h> 134 119 135 - #ifndef ACPI_HOTKEY_COMPONENT 136 - #define ACPI_HOTKEY_COMPONENT 0x10000000 137 - #endif 138 120 139 - #define _COMPONENT ACPI_HOTKEY_COMPONENT 140 - 141 - MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte"); 121 + MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>"); 122 + MODULE_AUTHOR("David Bronaugh <dbronaugh@linuxboxen.org>"); 123 + MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 124 + MODULE_AUTHOR("Martin Lucina <mato@kotelna.sk>"); 125 + MODULE_AUTHOR("Kenneth Chan <kenneth.t.chan@gmail.com>"); 142 126 MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops"); 143 127 MODULE_LICENSE("GPL"); 144 128 ··· 150 134 #define METHOD_HKEY_SQTY "SQTY" 151 135 #define METHOD_HKEY_SINF "SINF" 152 136 #define METHOD_HKEY_SSET "SSET" 153 - #define HKEY_NOTIFY 0x80 137 + #define METHOD_ECWR "\\_SB.ECWR" 138 + #define HKEY_NOTIFY 0x80 139 + #define ECO_MODE_OFF 0x00 140 + #define ECO_MODE_ON 0x80 154 141 155 142 #define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support" 156 143 #define ACPI_PCC_DEVICE_NAME "Hotkey" ··· 162 143 #define ACPI_PCC_INPUT_PHYS "panasonic/hkey0" 163 144 164 145 /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent 165 - ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00 146 + ECO_MODEs: 0x03 = off, 0x83 = on 166 147 */ 167 148 enum SINF_BITS { SINF_NUM_BATTERIES = 0, 168 149 SINF_LCD_TYPE, ··· 174 155 SINF_DC_CUR_BRIGHT, 175 156 SINF_MUTE, 176 157 SINF_RESERVED, 177 - SINF_ENV_STATE, 158 + SINF_ECO_MODE = 0x0A, 159 + SINF_CUR_BRIGHT = 0x0D, 178 160 SINF_STICKY_KEY = 0x80, 179 161 }; 180 162 /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ ··· 228 208 struct pcc_acpi { 229 209 acpi_handle handle; 230 210 unsigned long num_sifr; 231 - int sticky_mode; 211 + int sticky_key; 212 + int eco_mode; 213 + int mute; 214 + int ac_brightness; 215 + int dc_brightness; 216 + int current_brightness; 232 217 u32 *sinf; 233 218 struct acpi_device *device; 234 219 struct input_dev *input_dev; 235 220 struct backlight_device *backlight; 221 + struct platform_device *platform; 236 222 }; 237 223 238 224 /* method access functions */ ··· 272 246 if (ACPI_SUCCESS(status)) 273 247 return s; 274 248 else { 275 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 276 - "evaluation error HKEY.SQTY\n")); 249 + pr_err("evaluation error HKEY.SQTY\n"); 277 250 return -EINVAL; 278 251 } 279 252 } ··· 287 262 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL, 288 263 &buffer); 289 264 if (ACPI_FAILURE(status)) { 290 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 291 - "evaluation error HKEY.SINF\n")); 265 + pr_err("evaluation error HKEY.SINF\n"); 292 266 return 0; 293 267 } 294 268 295 269 hkey = buffer.pointer; 296 270 if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { 297 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); 271 + pr_err("Invalid HKEY.SINF\n"); 298 272 status = AE_ERROR; 299 273 goto end; 300 274 } 301 275 302 276 if (pcc->num_sifr < hkey->package.count) { 303 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 304 - "SQTY reports bad SINF length\n")); 277 + pr_err("SQTY reports bad SINF length\n"); 305 278 status = AE_ERROR; 306 279 goto end; 307 280 } ··· 309 286 if (likely(element->type == ACPI_TYPE_INTEGER)) { 310 287 pcc->sinf[i] = element->integer.value; 311 288 } else 312 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 313 - "Invalid HKEY.SINF data\n")); 289 + pr_err("Invalid HKEY.SINF data\n"); 314 290 } 315 291 pcc->sinf[hkey->package.count] = -1; 316 292 ··· 367 345 }; 368 346 369 347 348 + /* returns ACPI_SUCCESS if methods to control optical drive are present */ 349 + 350 + static acpi_status check_optd_present(void) 351 + { 352 + acpi_status status = AE_OK; 353 + acpi_handle handle; 354 + 355 + status = acpi_get_handle(NULL, "\\_SB.STAT", &handle); 356 + if (ACPI_FAILURE(status)) 357 + goto out; 358 + status = acpi_get_handle(NULL, "\\_SB.FBAY", &handle); 359 + if (ACPI_FAILURE(status)) 360 + goto out; 361 + status = acpi_get_handle(NULL, "\\_SB.CDDI", &handle); 362 + if (ACPI_FAILURE(status)) 363 + goto out; 364 + 365 + out: 366 + return status; 367 + } 368 + 369 + /* get optical driver power state */ 370 + 371 + static int get_optd_power_state(void) 372 + { 373 + acpi_status status; 374 + unsigned long long state; 375 + int result; 376 + 377 + status = acpi_evaluate_integer(NULL, "\\_SB.STAT", NULL, &state); 378 + if (ACPI_FAILURE(status)) { 379 + pr_err("evaluation error _SB.STAT\n"); 380 + result = -EIO; 381 + goto out; 382 + } 383 + switch (state) { 384 + case 0: /* power off */ 385 + result = 0; 386 + break; 387 + case 0x0f: /* power on */ 388 + result = 1; 389 + break; 390 + default: 391 + result = -EIO; 392 + break; 393 + } 394 + 395 + out: 396 + return result; 397 + } 398 + 399 + /* set optical drive power state */ 400 + 401 + static int set_optd_power_state(int new_state) 402 + { 403 + int result; 404 + acpi_status status; 405 + 406 + result = get_optd_power_state(); 407 + if (result < 0) 408 + goto out; 409 + if (new_state == result) 410 + goto out; 411 + 412 + switch (new_state) { 413 + case 0: /* power off */ 414 + /* Call CDDR instead, since they both call the same method 415 + * while CDDI takes 1 arg and we are not quite sure what it is. 416 + */ 417 + status = acpi_evaluate_object(NULL, "\\_SB.CDDR", NULL, NULL); 418 + if (ACPI_FAILURE(status)) { 419 + pr_err("evaluation error _SB.CDDR\n"); 420 + result = -EIO; 421 + } 422 + break; 423 + case 1: /* power on */ 424 + status = acpi_evaluate_object(NULL, "\\_SB.FBAY", NULL, NULL); 425 + if (ACPI_FAILURE(status)) { 426 + pr_err("evaluation error _SB.FBAY\n"); 427 + result = -EIO; 428 + } 429 + break; 430 + default: 431 + result = -EINVAL; 432 + break; 433 + } 434 + 435 + out: 436 + return result; 437 + } 438 + 439 + 370 440 /* sysfs user interface functions */ 371 441 372 - static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, 442 + static ssize_t numbatt_show(struct device *dev, struct device_attribute *attr, 373 443 char *buf) 374 444 { 375 445 struct acpi_device *acpi = to_acpi_device(dev); ··· 473 359 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); 474 360 } 475 361 476 - static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, 362 + static ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr, 477 363 char *buf) 478 364 { 479 365 struct acpi_device *acpi = to_acpi_device(dev); ··· 485 371 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); 486 372 } 487 373 488 - static ssize_t show_mute(struct device *dev, struct device_attribute *attr, 374 + static ssize_t mute_show(struct device *dev, struct device_attribute *attr, 489 375 char *buf) 490 376 { 491 377 struct acpi_device *acpi = to_acpi_device(dev); ··· 497 383 return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); 498 384 } 499 385 500 - static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, 386 + static ssize_t mute_store(struct device *dev, struct device_attribute *attr, 387 + const char *buf, size_t count) 388 + { 389 + struct acpi_device *acpi = to_acpi_device(dev); 390 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 391 + int err, val; 392 + 393 + err = kstrtoint(buf, 0, &val); 394 + if (err) 395 + return err; 396 + if (val == 0 || val == 1) { 397 + acpi_pcc_write_sset(pcc, SINF_MUTE, val); 398 + pcc->mute = val; 399 + } 400 + 401 + return count; 402 + } 403 + 404 + static ssize_t sticky_key_show(struct device *dev, struct device_attribute *attr, 501 405 char *buf) 502 406 { 503 407 struct acpi_device *acpi = to_acpi_device(dev); ··· 524 392 if (!acpi_pcc_retrieve_biosdata(pcc)) 525 393 return -EIO; 526 394 527 - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); 395 + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sticky_key); 528 396 } 529 397 530 - static ssize_t set_sticky(struct device *dev, struct device_attribute *attr, 398 + static ssize_t sticky_key_store(struct device *dev, struct device_attribute *attr, 531 399 const char *buf, size_t count) 532 400 { 533 401 struct acpi_device *acpi = to_acpi_device(dev); 534 402 struct pcc_acpi *pcc = acpi_driver_data(acpi); 535 - int val; 403 + int err, val; 536 404 537 - if (count && sscanf(buf, "%i", &val) == 1 && 538 - (val == 0 || val == 1)) { 405 + err = kstrtoint(buf, 0, &val); 406 + if (err) 407 + return err; 408 + if (val == 0 || val == 1) { 539 409 acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val); 540 - pcc->sticky_mode = val; 410 + pcc->sticky_key = val; 541 411 } 542 412 543 413 return count; 544 414 } 545 415 546 - static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL); 547 - static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL); 548 - static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL); 549 - static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky); 416 + static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr, 417 + char *buf) 418 + { 419 + struct acpi_device *acpi = to_acpi_device(dev); 420 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 421 + int result; 422 + 423 + if (!acpi_pcc_retrieve_biosdata(pcc)) 424 + return -EIO; 425 + 426 + switch (pcc->sinf[SINF_ECO_MODE]) { 427 + case (ECO_MODE_OFF + 3): 428 + result = 0; 429 + break; 430 + case (ECO_MODE_ON + 3): 431 + result = 1; 432 + break; 433 + default: 434 + result = -EIO; 435 + break; 436 + } 437 + return snprintf(buf, PAGE_SIZE, "%u\n", result); 438 + } 439 + 440 + static ssize_t eco_mode_store(struct device *dev, struct device_attribute *attr, 441 + const char *buf, size_t count) 442 + { 443 + struct acpi_device *acpi = to_acpi_device(dev); 444 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 445 + int err, state; 446 + 447 + union acpi_object param[2]; 448 + struct acpi_object_list input; 449 + acpi_status status; 450 + 451 + param[0].type = ACPI_TYPE_INTEGER; 452 + param[0].integer.value = 0x15; 453 + param[1].type = ACPI_TYPE_INTEGER; 454 + input.count = 2; 455 + input.pointer = param; 456 + 457 + err = kstrtoint(buf, 0, &state); 458 + if (err) 459 + return err; 460 + 461 + switch (state) { 462 + case 0: 463 + param[1].integer.value = ECO_MODE_OFF; 464 + pcc->sinf[SINF_ECO_MODE] = 0; 465 + pcc->eco_mode = 0; 466 + break; 467 + case 1: 468 + param[1].integer.value = ECO_MODE_ON; 469 + pcc->sinf[SINF_ECO_MODE] = 1; 470 + pcc->eco_mode = 1; 471 + break; 472 + default: 473 + /* nothing to do */ 474 + return count; 475 + } 476 + 477 + status = acpi_evaluate_object(NULL, METHOD_ECWR, 478 + &input, NULL); 479 + if (ACPI_FAILURE(status)) { 480 + pr_err("%s evaluation failed\n", METHOD_ECWR); 481 + return -EINVAL; 482 + } 483 + 484 + return count; 485 + } 486 + 487 + static ssize_t ac_brightness_show(struct device *dev, struct device_attribute *attr, 488 + char *buf) 489 + { 490 + struct acpi_device *acpi = to_acpi_device(dev); 491 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 492 + 493 + if (!acpi_pcc_retrieve_biosdata(pcc)) 494 + return -EIO; 495 + 496 + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]); 497 + } 498 + 499 + static ssize_t ac_brightness_store(struct device *dev, struct device_attribute *attr, 500 + const char *buf, size_t count) 501 + { 502 + struct acpi_device *acpi = to_acpi_device(dev); 503 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 504 + int err, val; 505 + 506 + err = kstrtoint(buf, 0, &val); 507 + if (err) 508 + return err; 509 + if (val >= 0 && val <= 255) { 510 + acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, val); 511 + pcc->ac_brightness = val; 512 + } 513 + 514 + return count; 515 + } 516 + 517 + static ssize_t dc_brightness_show(struct device *dev, struct device_attribute *attr, 518 + char *buf) 519 + { 520 + struct acpi_device *acpi = to_acpi_device(dev); 521 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 522 + 523 + if (!acpi_pcc_retrieve_biosdata(pcc)) 524 + return -EIO; 525 + 526 + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]); 527 + } 528 + 529 + static ssize_t dc_brightness_store(struct device *dev, struct device_attribute *attr, 530 + const char *buf, size_t count) 531 + { 532 + struct acpi_device *acpi = to_acpi_device(dev); 533 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 534 + int err, val; 535 + 536 + err = kstrtoint(buf, 0, &val); 537 + if (err) 538 + return err; 539 + if (val >= 0 && val <= 255) { 540 + acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, val); 541 + pcc->dc_brightness = val; 542 + } 543 + 544 + return count; 545 + } 546 + 547 + static ssize_t current_brightness_show(struct device *dev, struct device_attribute *attr, 548 + char *buf) 549 + { 550 + struct acpi_device *acpi = to_acpi_device(dev); 551 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 552 + 553 + if (!acpi_pcc_retrieve_biosdata(pcc)) 554 + return -EIO; 555 + 556 + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]); 557 + } 558 + 559 + static ssize_t current_brightness_store(struct device *dev, struct device_attribute *attr, 560 + const char *buf, size_t count) 561 + { 562 + struct acpi_device *acpi = to_acpi_device(dev); 563 + struct pcc_acpi *pcc = acpi_driver_data(acpi); 564 + int err, val; 565 + 566 + err = kstrtoint(buf, 0, &val); 567 + if (err) 568 + return err; 569 + 570 + if (val >= 0 && val <= 255) { 571 + err = acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, val); 572 + pcc->current_brightness = val; 573 + } 574 + 575 + return count; 576 + } 577 + 578 + static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr, 579 + char *buf) 580 + { 581 + return snprintf(buf, PAGE_SIZE, "%d\n", get_optd_power_state()); 582 + } 583 + 584 + static ssize_t cdpower_store(struct device *dev, struct device_attribute *attr, 585 + const char *buf, size_t count) 586 + { 587 + int err, val; 588 + 589 + err = kstrtoint(buf, 10, &val); 590 + if (err) 591 + return err; 592 + set_optd_power_state(val); 593 + return count; 594 + } 595 + 596 + static DEVICE_ATTR_RO(numbatt); 597 + static DEVICE_ATTR_RO(lcdtype); 598 + static DEVICE_ATTR_RW(mute); 599 + static DEVICE_ATTR_RW(sticky_key); 600 + static DEVICE_ATTR_RW(eco_mode); 601 + static DEVICE_ATTR_RW(ac_brightness); 602 + static DEVICE_ATTR_RW(dc_brightness); 603 + static DEVICE_ATTR_RW(current_brightness); 604 + static DEVICE_ATTR_RW(cdpower); 550 605 551 606 static struct attribute *pcc_sysfs_entries[] = { 552 607 &dev_attr_numbatt.attr, 553 608 &dev_attr_lcdtype.attr, 554 609 &dev_attr_mute.attr, 555 610 &dev_attr_sticky_key.attr, 611 + &dev_attr_eco_mode.attr, 612 + &dev_attr_ac_brightness.attr, 613 + &dev_attr_dc_brightness.attr, 614 + &dev_attr_current_brightness.attr, 615 + &dev_attr_cdpower.attr, 556 616 NULL, 557 617 }; 558 618 ··· 766 442 rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, 767 443 NULL, &result); 768 444 if (ACPI_FAILURE(rc)) { 769 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 770 - "error getting hotkey status\n")); 445 + pr_err("error getting hotkey status\n"); 771 446 return; 772 447 } 773 448 ··· 779 456 result & 0xf, 0x80, false); 780 457 } 781 458 782 - if (!sparse_keymap_report_event(hotk_input_dev, 783 - result & 0xf, result & 0x80, false)) 784 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 785 - "Unknown hotkey event: %d\n", result)); 459 + if ((result & 0xf) == 0x7 || (result & 0xf) == 0x9 || (result & 0xf) == 0xa) { 460 + if (!sparse_keymap_report_event(hotk_input_dev, 461 + result & 0xf, result & 0x80, false)) 462 + pr_err("Unknown hotkey event: 0x%04llx\n", result); 463 + } 786 464 } 787 465 788 466 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) ··· 797 473 default: 798 474 /* nothing to do */ 799 475 break; 476 + } 477 + } 478 + 479 + static void pcc_optd_notify(acpi_handle handle, u32 event, void *data) 480 + { 481 + if (event != ACPI_NOTIFY_EJECT_REQUEST) 482 + return; 483 + 484 + set_optd_power_state(0); 485 + } 486 + 487 + static int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) 488 + { 489 + acpi_status status; 490 + acpi_handle handle; 491 + 492 + status = acpi_get_handle(NULL, node, &handle); 493 + 494 + if (ACPI_SUCCESS(status)) { 495 + status = acpi_install_notify_handler(handle, 496 + ACPI_SYSTEM_NOTIFY, 497 + pcc_optd_notify, pcc); 498 + if (ACPI_FAILURE(status)) 499 + pr_err("Failed to register notify on %s\n", node); 500 + } else 501 + return -ENODEV; 502 + 503 + return 0; 504 + } 505 + 506 + static void pcc_unregister_optd_notifier(struct pcc_acpi *pcc, char *node) 507 + { 508 + acpi_status status = AE_OK; 509 + acpi_handle handle; 510 + 511 + status = acpi_get_handle(NULL, node, &handle); 512 + 513 + if (ACPI_SUCCESS(status)) { 514 + status = acpi_remove_notify_handler(handle, 515 + ACPI_SYSTEM_NOTIFY, 516 + pcc_optd_notify); 517 + if (ACPI_FAILURE(status)) 518 + pr_err("Error removing optd notify handler %s\n", 519 + node); 800 520 } 801 521 } 802 522 ··· 862 494 863 495 error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); 864 496 if (error) { 865 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 866 - "Unable to setup input device keymap\n")); 497 + pr_err("Unable to setup input device keymap\n"); 867 498 goto err_free_dev; 868 499 } 869 500 870 501 error = input_register_device(input_dev); 871 502 if (error) { 872 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 873 - "Unable to register input device\n")); 503 + pr_err("Unable to register input device\n"); 874 504 goto err_free_dev; 875 505 } 876 506 ··· 894 528 if (!pcc) 895 529 return -EINVAL; 896 530 897 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", 898 - pcc->sticky_mode)); 531 + acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); 532 + acpi_pcc_write_sset(pcc, SINF_ECO_MODE, pcc->eco_mode); 533 + acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_key); 534 + acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, pcc->ac_brightness); 535 + acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, pcc->dc_brightness); 536 + acpi_pcc_write_sset(pcc, SINF_CUR_BRIGHT, pcc->current_brightness); 899 537 900 - return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode); 538 + return 0; 901 539 } 902 540 #endif 903 541 ··· 917 547 num_sifr = acpi_pcc_get_sqty(device); 918 548 919 549 if (num_sifr < 0 || num_sifr > 255) { 920 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range")); 550 + pr_err("num_sifr out of range"); 921 551 return -ENODEV; 922 552 } 923 553 924 554 pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL); 925 555 if (!pcc) { 926 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 927 - "Couldn't allocate mem for pcc")); 556 + pr_err("Couldn't allocate mem for pcc"); 928 557 return -ENOMEM; 929 558 } 930 559 ··· 942 573 943 574 result = acpi_pcc_init_input(pcc); 944 575 if (result) { 945 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 946 - "Error installing keyinput handler\n")); 576 + pr_err("Error installing keyinput handler\n"); 947 577 goto out_sinf; 948 578 } 949 579 950 580 if (!acpi_pcc_retrieve_biosdata(pcc)) { 951 - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 952 - "Couldn't retrieve BIOS data\n")); 953 581 result = -EIO; 582 + pr_err("Couldn't retrieve BIOS data\n"); 954 583 goto out_input; 955 584 } 956 585 /* initialize backlight */ ··· 965 598 /* read the initial brightness setting from the hardware */ 966 599 pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; 967 600 968 - /* read the initial sticky key mode from the hardware */ 969 - pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY]; 601 + /* Reset initial sticky key mode since the hardware register state is not consistent */ 602 + acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0); 603 + pcc->sticky_key = 0; 604 + 605 + pcc->eco_mode = pcc->sinf[SINF_ECO_MODE]; 606 + pcc->mute = pcc->sinf[SINF_MUTE]; 607 + pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; 608 + pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT]; 609 + result = pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT]; 970 610 971 611 /* add sysfs attributes */ 972 612 result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group); 973 613 if (result) 974 614 goto out_backlight; 975 615 616 + /* optical drive initialization */ 617 + if (ACPI_SUCCESS(check_optd_present())) { 618 + pcc->platform = platform_device_register_simple("panasonic", 619 + -1, NULL, 0); 620 + if (IS_ERR(pcc->platform)) { 621 + result = PTR_ERR(pcc->platform); 622 + goto out_backlight; 623 + } 624 + result = device_create_file(&pcc->platform->dev, 625 + &dev_attr_cdpower); 626 + pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); 627 + if (result) 628 + goto out_platform; 629 + } else { 630 + pcc->platform = NULL; 631 + } 632 + 976 633 return 0; 977 634 635 + out_platform: 636 + platform_device_unregister(pcc->platform); 978 637 out_backlight: 979 638 backlight_device_unregister(pcc->backlight); 980 639 out_input: ··· 1019 626 1020 627 if (!device || !pcc) 1021 628 return -EINVAL; 629 + 630 + if (pcc->platform) { 631 + device_remove_file(&pcc->platform->dev, &dev_attr_cdpower); 632 + platform_device_unregister(pcc->platform); 633 + } 634 + pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); 1022 635 1023 636 sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); 1024 637
-3
drivers/platform/x86/sony-laptop.c
··· 2467 2467 * 0: integrated GFX (stamina) 2468 2468 */ 2469 2469 return result & 0x1 ? SPEED : STAMINA; 2470 - break; 2471 2470 case 0x015B: 2472 2471 /* 0: discrete GFX (speed) 2473 2472 * 1: integrated GFX (stamina) 2474 2473 */ 2475 2474 return result & 0x1 ? STAMINA : SPEED; 2476 - break; 2477 2475 case 0x0128: 2478 2476 /* it's a more elaborated bitmask, for now: 2479 2477 * 2: integrated GFX (stamina) ··· 2480 2482 dprintk("GFX Status: 0x%x\n", result); 2481 2483 return result & 0x80 ? AUTO : 2482 2484 result & 0x02 ? STAMINA : SPEED; 2483 - break; 2484 2485 } 2485 2486 return -EINVAL; 2486 2487 }
drivers/platform/x86/surface3-wmi.c drivers/platform/surface/surface3-wmi.c
drivers/platform/x86/surface3_button.c drivers/platform/surface/surface3_button.c
drivers/platform/x86/surface3_power.c drivers/platform/surface/surface3_power.c
drivers/platform/x86/surfacepro3_button.c drivers/platform/surface/surfacepro3_button.c
+108 -64
drivers/platform/x86/thinkpad_acpi.c
··· 1025 1025 } 1026 1026 1027 1027 #define destroy_attr_set(_set) \ 1028 - kfree(_set); 1028 + kfree(_set) 1029 1029 1030 1030 /* not multi-threaded safe, use it in a single thread per set */ 1031 1031 static int add_to_attr_set(struct attribute_set *s, struct attribute *attr) ··· 4028 4028 } 4029 4029 4030 4030 static void thermal_dump_all_sensors(void); 4031 + static void palmsensor_refresh(void); 4031 4032 4032 4033 static bool hotkey_notify_6xxx(const u32 hkey, 4033 4034 bool *send_acpi_ev, ··· 4095 4094 4096 4095 case TP_HKEY_EV_PALM_DETECTED: 4097 4096 case TP_HKEY_EV_PALM_UNDETECTED: 4098 - /* palm detected hovering the keyboard, forward to user-space 4099 - * via netlink for consumption */ 4097 + /* palm detected - pass on to event handler */ 4098 + palmsensor_refresh(); 4100 4099 return true; 4101 4100 4102 4101 default: ··· 9840 9839 }; 9841 9840 9842 9841 /************************************************************************* 9843 - * DYTC subdriver, for the Lenovo lapmode feature 9842 + * Thinkpad sensor interfaces 9844 9843 */ 9845 9844 9846 9845 #define DYTC_CMD_GET 2 /* To get current IC function and mode */ 9847 9846 #define DYTC_GET_LAPMODE_BIT 17 /* Set when in lapmode */ 9848 9847 9849 - static bool dytc_lapmode; 9848 + #define PALMSENSOR_PRESENT_BIT 0 /* Determine if psensor present */ 9849 + #define PALMSENSOR_ON_BIT 1 /* psensor status */ 9850 9850 9851 - static void dytc_lapmode_notify_change(void) 9852 - { 9853 - sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode"); 9854 - } 9851 + static bool has_palmsensor; 9852 + static bool has_lapsensor; 9853 + static bool palm_state; 9854 + static bool lap_state; 9855 9855 9856 - static int dytc_command(int command, int *output) 9856 + static int lapsensor_get(bool *present, bool *state) 9857 9857 { 9858 9858 acpi_handle dytc_handle; 9859 + int output; 9859 9860 9860 - if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) { 9861 - /* Platform doesn't support DYTC */ 9861 + *present = false; 9862 + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DYTC", &dytc_handle))) 9862 9863 return -ENODEV; 9863 - } 9864 - if (!acpi_evalf(dytc_handle, output, NULL, "dd", command)) 9864 + if (!acpi_evalf(dytc_handle, &output, NULL, "dd", DYTC_CMD_GET)) 9865 9865 return -EIO; 9866 - return 0; 9867 - } 9868 9866 9869 - static int dytc_lapmode_get(bool *state) 9870 - { 9871 - int output, err; 9872 - 9873 - err = dytc_command(DYTC_CMD_GET, &output); 9874 - if (err) 9875 - return err; 9867 + *present = true; /*If we get his far, we have lapmode support*/ 9876 9868 *state = output & BIT(DYTC_GET_LAPMODE_BIT) ? true : false; 9877 9869 return 0; 9878 9870 } 9879 9871 9880 - static void dytc_lapmode_refresh(void) 9872 + static int palmsensor_get(bool *present, bool *state) 9881 9873 { 9882 - bool new_state; 9883 - int err; 9874 + acpi_handle psensor_handle; 9875 + int output; 9884 9876 9885 - err = dytc_lapmode_get(&new_state); 9886 - if (err || (new_state == dytc_lapmode)) 9887 - return; 9877 + *present = false; 9878 + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GPSS", &psensor_handle))) 9879 + return -ENODEV; 9880 + if (!acpi_evalf(psensor_handle, &output, NULL, "d")) 9881 + return -EIO; 9888 9882 9889 - dytc_lapmode = new_state; 9890 - dytc_lapmode_notify_change(); 9883 + *present = output & BIT(PALMSENSOR_PRESENT_BIT) ? true : false; 9884 + *state = output & BIT(PALMSENSOR_ON_BIT) ? true : false; 9885 + return 0; 9891 9886 } 9892 9887 9893 - /* sysfs lapmode entry */ 9888 + static void lapsensor_refresh(void) 9889 + { 9890 + bool state; 9891 + int err; 9892 + 9893 + if (has_lapsensor) { 9894 + err = lapsensor_get(&has_lapsensor, &state); 9895 + if (err) 9896 + return; 9897 + if (lap_state != state) { 9898 + lap_state = state; 9899 + sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "dytc_lapmode"); 9900 + } 9901 + } 9902 + } 9903 + 9904 + static void palmsensor_refresh(void) 9905 + { 9906 + bool state; 9907 + int err; 9908 + 9909 + if (has_palmsensor) { 9910 + err = palmsensor_get(&has_palmsensor, &state); 9911 + if (err) 9912 + return; 9913 + if (palm_state != state) { 9914 + palm_state = state; 9915 + sysfs_notify(&tpacpi_pdev->dev.kobj, NULL, "palmsensor"); 9916 + } 9917 + } 9918 + } 9919 + 9894 9920 static ssize_t dytc_lapmode_show(struct device *dev, 9895 9921 struct device_attribute *attr, 9896 9922 char *buf) 9897 9923 { 9898 - return snprintf(buf, PAGE_SIZE, "%d\n", dytc_lapmode); 9924 + if (has_lapsensor) 9925 + return sysfs_emit(buf, "%d\n", lap_state); 9926 + return sysfs_emit(buf, "\n"); 9899 9927 } 9900 - 9901 9928 static DEVICE_ATTR_RO(dytc_lapmode); 9902 9929 9903 - static struct attribute *dytc_attributes[] = { 9904 - &dev_attr_dytc_lapmode.attr, 9905 - NULL, 9906 - }; 9907 - 9908 - static const struct attribute_group dytc_attr_group = { 9909 - .attrs = dytc_attributes, 9910 - }; 9911 - 9912 - static int tpacpi_dytc_init(struct ibm_init_struct *iibm) 9930 + static ssize_t palmsensor_show(struct device *dev, 9931 + struct device_attribute *attr, 9932 + char *buf) 9913 9933 { 9914 - int err; 9934 + if (has_palmsensor) 9935 + return sysfs_emit(buf, "%d\n", palm_state); 9936 + return sysfs_emit(buf, "\n"); 9937 + } 9938 + static DEVICE_ATTR_RO(palmsensor); 9915 9939 9916 - err = dytc_lapmode_get(&dytc_lapmode); 9917 - /* If support isn't available (ENODEV) then don't return an error 9918 - * but just don't create the sysfs group 9940 + static int tpacpi_proxsensor_init(struct ibm_init_struct *iibm) 9941 + { 9942 + int palm_err, lap_err, err; 9943 + 9944 + palm_err = palmsensor_get(&has_palmsensor, &palm_state); 9945 + lap_err = lapsensor_get(&has_lapsensor, &lap_state); 9946 + /* 9947 + * If support isn't available (ENODEV) for both devices then quit, but 9948 + * don't return an error. 9919 9949 */ 9920 - if (err == -ENODEV) 9950 + if ((palm_err == -ENODEV) && (lap_err == -ENODEV)) 9921 9951 return 0; 9922 - /* For all other errors we can flag the failure */ 9923 - if (err) 9924 - return err; 9952 + /* Otherwise, if there was an error return it */ 9953 + if (palm_err && (palm_err != ENODEV)) 9954 + return palm_err; 9955 + if (lap_err && (lap_err != ENODEV)) 9956 + return lap_err; 9925 9957 9926 - /* Platform supports this feature - create the group */ 9927 - err = sysfs_create_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group); 9928 - return err; 9958 + if (has_palmsensor) { 9959 + err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr); 9960 + if (err) 9961 + return err; 9962 + } 9963 + if (has_lapsensor) { 9964 + err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr); 9965 + if (err) 9966 + return err; 9967 + } 9968 + return 0; 9929 9969 } 9930 9970 9931 - static void dytc_exit(void) 9971 + static void proxsensor_exit(void) 9932 9972 { 9933 - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &dytc_attr_group); 9973 + if (has_lapsensor) 9974 + sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_dytc_lapmode.attr); 9975 + if (has_palmsensor) 9976 + sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_palmsensor.attr); 9934 9977 } 9935 9978 9936 - static struct ibm_struct dytc_driver_data = { 9937 - .name = "dytc", 9938 - .exit = dytc_exit, 9979 + static struct ibm_struct proxsensor_driver_data = { 9980 + .name = "proximity-sensor", 9981 + .exit = proxsensor_exit, 9939 9982 }; 9940 9983 9941 9984 /**************************************************************************** ··· 10031 9986 } 10032 9987 10033 9988 if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) 10034 - dytc_lapmode_refresh(); 10035 - 9989 + lapsensor_refresh(); 10036 9990 } 10037 9991 10038 9992 static void hotkey_driver_event(const unsigned int scancode) ··· 10471 10427 .data = &lcdshadow_driver_data, 10472 10428 }, 10473 10429 { 10474 - .init = tpacpi_dytc_init, 10475 - .data = &dytc_driver_data, 10430 + .init = tpacpi_proxsensor_init, 10431 + .data = &proxsensor_driver_data, 10476 10432 }, 10477 10433 }; 10478 10434
+2 -5
drivers/platform/x86/wmi.c
··· 1260 1260 switch (result) { 1261 1261 case -EINVAL: 1262 1262 return AE_BAD_PARAMETER; 1263 - break; 1264 1263 case -ENODEV: 1265 1264 return AE_NOT_FOUND; 1266 - break; 1267 1265 case -ETIME: 1268 1266 return AE_TIME; 1269 - break; 1270 1267 default: 1271 1268 return AE_OK; 1272 1269 } ··· 1344 1347 acpi_remove_address_space_handler(acpi_device->handle, 1345 1348 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); 1346 1349 wmi_free_devices(acpi_device); 1347 - device_destroy(&wmi_bus_class, MKDEV(0, 0)); 1350 + device_unregister((struct device *)dev_get_drvdata(&device->dev)); 1348 1351 1349 1352 return 0; 1350 1353 } ··· 1398 1401 return 0; 1399 1402 1400 1403 err_remove_busdev: 1401 - device_destroy(&wmi_bus_class, MKDEV(0, 0)); 1404 + device_unregister(wmi_bus_dev); 1402 1405 1403 1406 err_remove_notify_handler: 1404 1407 acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
+1
include/linux/platform_data/x86/asus-wmi.h
··· 62 62 63 63 /* Misc */ 64 64 #define ASUS_WMI_DEVID_CAMERA 0x00060013 65 + #define ASUS_WMI_DEVID_LID_FLIP 0x00060062 65 66 66 67 /* Storage */ 67 68 #define ASUS_WMI_DEVID_CARDREADER 0x00080013
+5
include/uapi/linux/pci_regs.h
··· 723 723 #define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */ 724 724 #define PCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */ 725 725 #define PCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ 726 + #define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ 726 727 #define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ 727 728 #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ 728 729 #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_16GT ··· 1066 1065 #define PCI_L1SS_CTL1_LTR_L12_TH_VALUE 0x03ff0000 /* LTR_L1.2_THRESHOLD_Value */ 1067 1066 #define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ 1068 1067 #define PCI_L1SS_CTL2 0x0c /* Control 2 Register */ 1068 + 1069 + /* Designated Vendor-Specific (DVSEC, PCI_EXT_CAP_ID_DVSEC) */ 1070 + #define PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ 1071 + #define PCI_DVSEC_HEADER2 0x8 /* Designated Vendor-Specific Header2 */ 1069 1072 1070 1073 /* Data Link Feature */ 1071 1074 #define PCI_DLF_CAP 0x04 /* Capabilities Register */
+6 -2
tools/power/x86/intel-speed-select/isst-config.c
··· 15 15 int arg; 16 16 }; 17 17 18 - static const char *version_str = "v1.6"; 18 + static const char *version_str = "v1.7"; 19 19 static const int supported_api_ver = 1; 20 20 static struct isst_if_platform_info isst_platform_info; 21 21 static char *progname; ··· 328 328 int core_id, pkg_id, die_id; 329 329 330 330 ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id); 331 - if (!ret) 331 + if (!ret) { 332 + if (die_id < 0) 333 + die_id = 0; 334 + 332 335 return die_id; 336 + } 333 337 } 334 338 335 339 if (ret < 0)
+1 -1
tools/power/x86/intel-speed-select/isst-core.c
··· 804 804 return ret; 805 805 } 806 806 807 - if (!pkg_dev->enabled) { 807 + if (!pkg_dev->enabled && is_skx_based_platform()) { 808 808 int freq; 809 809 810 810 freq = get_cpufreq_base_freq(cpu);
+1
tools/power/x86/intel-speed-select/isst.h
··· 255 255 extern int get_cpufreq_base_freq(int cpu); 256 256 extern int isst_read_pm_config(int cpu, int *cp_state, int *cp_cap); 257 257 extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg); 258 + extern int is_skx_based_platform(void); 258 259 #endif