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

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

Pull x86 platform driver updates from Hans de Goede:

- hp-bioscfg: New firmware-attributes driver for changing BIOS settings
from within Linux

- asus-wmi: Add charger mode, middle fan and eGPU settings support

- ideapad: Support keyboard backlight control on more models

- mellanox: Support for new models

- sel-3350: New LED and power-supply driver for this industrial
mainboard

- simatic-ipc: Add RTC battery monitor and various new models support

- miscellaneous other cleanups / fixes

* tag 'platform-drivers-x86-v6.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (101 commits)
platform/x86: asus-wmi: corrections to egpu safety check
platform/x86: mlx-platform: Add dependency on PCI to Kconfig
platform/x86: ideapad-laptop: Add support for keyboard backlights using KBLC ACPI symbol
platform/x86/amd/pmc: Fix build error with randconfig
platform/x86/amd/pmf: Fix a missing cleanup path
watchdog: simatic: Use idiomatic selection of P2SB
platform/x86: p2sb: Make the Kconfig symbol hidden
Documentation/ABI: Add new attribute for mlxreg-io sysfs interfaces
platform: mellanox: nvsw-sn2201: change fans i2c busses.
platform: mellanox: mlxreg-hotplug: Extend condition for notification callback processing
platform: mellanox: Add initial support for PCIe based programming logic device
platform: mellanox: mlx-platform: Get interrupt line through ACPI
platform: mellanox: mlx-platform: Introduce ACPI init flow
platform: mellanox: mlx-platform: Prepare driver to allow probing through ACPI infrastructure
platform: mellanox: mlx-platform: Add reset callback
platform: mellanox: Cosmetic changes
platform: mellanox: mlx-platform: Modify power off callback
platform: mellanox: mlx-platform: add support for additional CPLD
platform: mellanox: mlx-platform: Add reset cause attribute
platform: mellanox: mlx-platform: Modify health and power hotplug action
...

+8318 -353
+53
Documentation/ABI/stable/sysfs-driver-mlxreg-io
··· 662 662 Value 1 in file means this is reset cause, 0 - otherwise. 663 663 664 664 The file is read only. 665 + 666 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld5_pn 667 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld5_version 668 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld5_version_min 669 + Date: August 2023 670 + KernelVersion: 6.6 671 + Contact: Vadim Pasternak <vadimp@nvidia.com> 672 + Description: These files show with which CPLD part numbers, version and minor 673 + versions have been burned the 5-th CPLD device equipped on a 674 + system. 675 + 676 + The files are read only. 677 + 678 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/jtag_cap 679 + Date: August 2023 680 + KernelVersion: 6.6 681 + Contact: Vadim Pasternak <vadimp@nvidia.com> 682 + Description: This file indicates the available method of CPLD/FPGA devices 683 + field update through the JTAG chain: 684 + 685 + b00 - field update through LPC bus register memory space. 686 + b01 - Reserved. 687 + b10 - Reserved. 688 + b11 - field update through CPU GPIOs bit-banging. 689 + 690 + The file is read only. 691 + 692 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/lid_open 693 + Date: August 2023 694 + KernelVersion: 6.6 695 + Contact: Vadim Pasternak <vadimp@nvidia.com> 696 + Description: 1 - indicates that system lid is opened, otherwise 0. 697 + 698 + The file is read only. 699 + 700 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_long_pwr_pb 701 + Date: August 2023 702 + KernelVersion: 6.6 703 + Contact: Vadim Pasternak <vadimp@nvidia.com> 704 + Description: This file if set 1 indicates that system has been reset by 705 + long press of power button. 706 + 707 + The file is read only. 708 + 709 + What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_swb_dc_dc_pwr_fail 710 + Date: August 2023 711 + KernelVersion: 6.6 712 + Contact: Vadim Pasternak <vadimp@nvidia.com> 713 + Description: This file shows 1 in case the system reset happened due to the 714 + failure of any DC-DC power converter devices equipped on the 715 + switch board. 716 + 717 + The file is read only.
+31
Documentation/ABI/testing/debugfs-tpmi
··· 1 + What: /sys/kernel/debug/tpmi-<n>/pfs_dump 2 + Date: November 2023 3 + KernelVersion: 6.6 4 + Contact: srinivas.pandruvada@linux.intel.com 5 + Description: 6 + The PFS (PM Feature Structure) table, shows details of each power 7 + management feature. This includes: 8 + tpmi_id, number of entries, entry size, offset, vsec offset, lock status 9 + and disabled status. 10 + Users: Debugging, any user space test suite 11 + 12 + What: /sys/kernel/debug/tpmi-<n>/tpmi-id-<n>/mem_dump 13 + Date: November 2023 14 + KernelVersion: 6.6 15 + Contact: srinivas.pandruvada@linux.intel.com 16 + Description: 17 + Shows the memory dump of the MMIO region for a TPMI ID. 18 + Users: Debugging, any user space test suite 19 + 20 + What: /sys/kernel/debug/tpmi-<n>/tpmi-id-<n>/mem_write 21 + Date: November 2023 22 + KernelVersion: 6.6 23 + Contact: srinivas.pandruvada@linux.intel.com 24 + Description: 25 + Allows to write at any offset. It doesn't check for Read/Write access 26 + as hardware will not allow to write at read-only memory. This write is 27 + at offset multiples of 4. The format is instance,offset,contents. 28 + Example: 29 + echo 0,0x20,0xff > mem_write 30 + echo 1,64,64 > mem_write 31 + Users: Debugging, any user space test suite
+99 -2
Documentation/ABI/testing/sysfs-class-firmware-attributes
··· 22 22 - integer: a range of numerical values 23 23 - string 24 24 25 + HP specific types 26 + ----------------- 27 + - ordered-list - a set of ordered list valid values 28 + 29 + 25 30 All attribute types support the following values: 26 31 27 32 current_value: ··· 131 126 value will not be effective through sysfs until this rule is 132 127 met. 133 128 129 + HP specific class extensions 130 + ------------------------------ 131 + 132 + On HP systems the following additional attributes are available: 133 + 134 + "ordered-list"-type specific properties: 135 + 136 + elements: 137 + A file that can be read to obtain the possible 138 + list of values of the <attr>. Values are separated using 139 + semi-colon (``;``) and listed according to their priority. 140 + An element listed first has the highest priority. Writing 141 + the list in a different order to current_value alters 142 + the priority order for the particular attribute. 143 + 134 144 What: /sys/class/firmware-attributes/*/authentication/ 135 145 Date: February 2021 136 146 KernelVersion: 5.11 ··· 226 206 Drivers may emit a CHANGE uevent when a password is set or unset 227 207 userspace may check it again. 228 208 229 - On Dell and Lenovo systems, if Admin password is set, then all BIOS attributes 209 + On Dell, Lenovo and HP systems, if Admin password is set, then all BIOS attributes 230 210 require password validation. 231 211 On Lenovo systems if you change the Admin password the new password is not active until 232 212 the next boot. ··· 316 296 echo "signature" > authentication/Admin/signature 317 297 echo "password" > authentication/Admin/certificate_to_password 318 298 299 + HP specific class extensions 300 + -------------------------------- 301 + 302 + On HP systems the following additional settings are available: 303 + 304 + role: enhanced-bios-auth: 305 + This role is specific to Secure Platform Management (SPM) attribute. 306 + It requires configuring an endorsement (kek) and signing certificate (sk). 307 + 319 308 320 309 What: /sys/class/firmware-attributes/*/attributes/pending_reboot 321 310 Date: February 2021 ··· 340 311 == ========================================= 341 312 0 All BIOS attributes setting are current 342 313 1 A reboot is necessary to get pending BIOS 343 - attribute changes applied 314 + attribute changes applied 344 315 == ========================================= 345 316 346 317 Note, userspace applications need to follow below steps for efficient ··· 393 364 use it to enable extra debug attributes or BIOS features for testing purposes. 394 365 395 366 Note that any changes to this attribute requires a reboot for changes to take effect. 367 + 368 + 369 + HP specific class extensions - Secure Platform Manager (SPM) 370 + -------------------------------- 371 + 372 + What: /sys/class/firmware-attributes/*/authentication/SPM/kek 373 + Date: March 2023 374 + KernelVersion: 5.18 375 + Contact: "Jorge Lopez" <jorge.lopez2@hp.com> 376 + Description: 377 + 'kek' Key-Encryption-Key is a write-only file that can be used to configure the 378 + RSA public key that will be used by the BIOS to verify 379 + signatures when setting the signing key. When written, 380 + the bytes should correspond to the KEK certificate 381 + (x509 .DER format containing an OU). The size of the 382 + certificate must be less than or equal to 4095 bytes. 383 + 384 + What: /sys/class/firmware-attributes/*/authentication/SPM/sk 385 + Date: March 2023 386 + KernelVersion: 5.18 387 + Contact: "Jorge Lopez" <jorge.lopez2@hp.com> 388 + Description: 389 + 'sk' Signature Key is a write-only file that can be used to configure the RSA 390 + public key that will be used by the BIOS to verify signatures 391 + when configuring BIOS settings and security features. When 392 + written, the bytes should correspond to the modulus of the 393 + public key. The exponent is assumed to be 0x10001. 394 + 395 + What: /sys/class/firmware-attributes/*/authentication/SPM/status 396 + Date: March 2023 397 + KernelVersion: 5.18 398 + Contact: "Jorge Lopez" <jorge.lopez2@hp.com> 399 + Description: 400 + 'status' is a read-only file that returns ASCII text in JSON format reporting 401 + the status information. 402 + 403 + "State": "not provisioned | provisioned | provisioning in progress", 404 + "Version": "Major.Minor", 405 + "Nonce": <16-bit unsigned number display in base 10>, 406 + "FeaturesInUse": <16-bit unsigned number display in base 10>, 407 + "EndorsementKeyMod": "<256 bytes in base64>", 408 + "SigningKeyMod": "<256 bytes in base64>" 409 + 410 + What: /sys/class/firmware-attributes/*/attributes/Sure_Start/audit_log_entries 411 + Date: March 2023 412 + KernelVersion: 5.18 413 + Contact: "Jorge Lopez" <jorge.lopez2@hp.com> 414 + Description: 415 + 'audit_log_entries' is a read-only file that returns the events in the log. 416 + 417 + Audit log entry format 418 + 419 + Byte 0-15: Requested Audit Log entry (Each Audit log is 16 bytes) 420 + Byte 16-127: Unused 421 + 422 + What: /sys/class/firmware-attributes/*/attributes/Sure_Start/audit_log_entry_count 423 + Date: March 2023 424 + KernelVersion: 5.18 425 + Contact: "Jorge Lopez" <jorge.lopez2@hp.com> 426 + Description: 427 + 'audit_log_entry_count' is a read-only file that returns the number of existing 428 + audit log events available to be read. Values are separated using comma. (``,``) 429 + 430 + [No of entries],[log entry size],[Max number of entries supported] 431 + 432 + log entry size identifies audit log size for the current BIOS version. 433 + The current size is 16 bytes but it can be up to 128 bytes long in future BIOS 434 + versions.
+88
Documentation/ABI/testing/sysfs-platform-asus-wmi
··· 98 98 Enable an LCD response-time boost to reduce or remove ghosting: 99 99 * 0 - Disable, 100 100 * 1 - Enable 101 + 102 + What: /sys/devices/platform/<platform>/charge_mode 103 + Date: Jun 2023 104 + KernelVersion: 6.5 105 + Contact: "Luke Jones" <luke@ljones.dev> 106 + Description: 107 + Get the current charging mode being used: 108 + * 1 - Barrel connected charger, 109 + * 2 - USB-C charging 110 + * 3 - Both connected, barrel used for charging 111 + 112 + What: /sys/devices/platform/<platform>/egpu_connected 113 + Date: Jun 2023 114 + KernelVersion: 6.5 115 + Contact: "Luke Jones" <luke@ljones.dev> 116 + Description: 117 + Show if the egpu (XG Mobile) is correctly connected: 118 + * 0 - False, 119 + * 1 - True 120 + 121 + What: /sys/devices/platform/<platform>/mini_led_mode 122 + Date: Jun 2023 123 + KernelVersion: 6.5 124 + Contact: "Luke Jones" <luke@ljones.dev> 125 + Description: 126 + Change the mini-LED mode: 127 + * 0 - Single-zone, 128 + * 1 - Multi-zone 129 + 130 + What: /sys/devices/platform/<platform>/ppt_pl1_spl 131 + Date: Jun 2023 132 + KernelVersion: 6.5 133 + Contact: "Luke Jones" <luke@ljones.dev> 134 + Description: 135 + Set the Package Power Target total of CPU: PL1 on Intel, SPL on AMD. 136 + Shown on Intel+Nvidia or AMD+Nvidia based systems: 137 + 138 + * min=5, max=250 139 + 140 + What: /sys/devices/platform/<platform>/ppt_pl2_sppt 141 + Date: Jun 2023 142 + KernelVersion: 6.5 143 + Contact: "Luke Jones" <luke@ljones.dev> 144 + Description: 145 + Set the Slow Package Power Tracking Limit of CPU: PL2 on Intel, SPPT, 146 + on AMD. Shown on Intel+Nvidia or AMD+Nvidia based systems: 147 + 148 + * min=5, max=250 149 + 150 + What: /sys/devices/platform/<platform>/ppt_fppt 151 + Date: Jun 2023 152 + KernelVersion: 6.5 153 + Contact: "Luke Jones" <luke@ljones.dev> 154 + Description: 155 + Set the Fast Package Power Tracking Limit of CPU. AMD+Nvidia only: 156 + * min=5, max=250 157 + 158 + What: /sys/devices/platform/<platform>/ppt_apu_sppt 159 + Date: Jun 2023 160 + KernelVersion: 6.5 161 + Contact: "Luke Jones" <luke@ljones.dev> 162 + Description: 163 + Set the APU SPPT limit. Shown on full AMD systems only: 164 + * min=5, max=130 165 + 166 + What: /sys/devices/platform/<platform>/ppt_platform_sppt 167 + Date: Jun 2023 168 + KernelVersion: 6.5 169 + Contact: "Luke Jones" <luke@ljones.dev> 170 + Description: 171 + Set the platform SPPT limit. Shown on full AMD systems only: 172 + * min=5, max=130 173 + 174 + What: /sys/devices/platform/<platform>/nv_dynamic_boost 175 + Date: Jun 2023 176 + KernelVersion: 6.5 177 + Contact: "Luke Jones" <luke@ljones.dev> 178 + Description: 179 + Set the dynamic boost limit of the Nvidia dGPU: 180 + * min=5, max=25 181 + 182 + What: /sys/devices/platform/<platform>/nv_temp_target 183 + Date: Jun 2023 184 + KernelVersion: 6.5 185 + Contact: "Luke Jones" <luke@ljones.dev> 186 + Description: 187 + Set the target temperature limit of the Nvidia dGPU: 188 + * min=75, max=87
+66
Documentation/ABI/testing/sysfs-platform-mellanox-bootctl
··· 84 84 The file used to write BlueField boot log with the format 85 85 "[INFO|WARN|ERR|ASSERT ]<msg>". Log level 'INFO' is used by 86 86 default if not specified. 87 + 88 + What: /sys/bus/platform/devices/MLNXBF04:00/oob_mac 89 + Date: August 2023 90 + KernelVersion: 6.5 91 + Contact: "David Thompson <davthompson@nvidia.com>" 92 + Description: 93 + The "oob_mac" sysfs attribute holds the MAC address for 94 + the out-of-band 1Gbps Ethernet port. This MAC address is 95 + provided on a board-level label. 96 + 97 + What: /sys/bus/platform/devices/MLNXBF04:00/opn 98 + Date: August 2023 99 + KernelVersion: 6.5 100 + Contact: "David Thompson <davthompson@nvidia.com>" 101 + Description: 102 + The "opn" sysfs attribute holds the board's part number. 103 + This value is provided on a board-level label. 104 + 105 + What: /sys/bus/platform/devices/MLNXBF04:00/sku 106 + Date: August 2023 107 + KernelVersion: 6.5 108 + Contact: "David Thompson <davthompson@nvidia.com>" 109 + Description: 110 + The "sku" sysfs attribute holds the board's SKU number. 111 + This value is provided on a board-level label. 112 + 113 + What: /sys/bus/platform/devices/MLNXBF04:00/modl 114 + Date: August 2023 115 + KernelVersion: 6.5 116 + Contact: "David Thompson <davthompson@nvidia.com>" 117 + Description: 118 + The "modl" sysfs attribute holds the board's model number. 119 + This value is provided on a board-level label. 120 + 121 + What: /sys/bus/platform/devices/MLNXBF04:00/sn 122 + Date: August 2023 123 + KernelVersion: 6.5 124 + Contact: "David Thompson <davthompson@nvidia.com>" 125 + Description: 126 + The "sn" sysfs attribute holds the board's serial number. 127 + This value is provided on a board-level label. 128 + 129 + What: /sys/bus/platform/devices/MLNXBF04:00/uuid 130 + Date: August 2023 131 + KernelVersion: 6.5 132 + Contact: "David Thompson <davthompson@nvidia.com>" 133 + Description: 134 + The "uuid" sysfs attribute holds the board's UUID. 135 + This value is provided by the manufacturing team. 136 + 137 + What: /sys/bus/platform/devices/MLNXBF04:00/rev 138 + Date: August 2023 139 + KernelVersion: 6.5 140 + Contact: "David Thompson <davthompson@nvidia.com>" 141 + Description: 142 + The "rev" sysfs attribute holds the board's revision. 143 + This value is provided on a board-level label. 144 + 145 + What: /sys/bus/platform/devices/MLNXBF04:00/mfg_lock 146 + Date: August 2023 147 + KernelVersion: 6.5 148 + Contact: "David Thompson <davthompson@nvidia.com>" 149 + Description: 150 + The "mfg_lock" sysfs attribute is write-only. 151 + A successful write to this attribute will latch the 152 + board-level attributes into EEPROM, making them read-only.
+38 -3
MAINTAINERS
··· 1018 1018 M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 1019 1019 L: platform-driver-x86@vger.kernel.org 1020 1020 S: Maintained 1021 - F: drivers/platform/x86/amd/pmc.c 1021 + F: drivers/platform/x86/amd/pmc/ 1022 1022 1023 1023 AMD PMF DRIVER 1024 1024 M: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> ··· 9522 9522 W: http://w1.fi/hostap-driver.html 9523 9523 F: drivers/net/wireless/intersil/hostap/ 9524 9524 9525 + HP BIOSCFG DRIVER 9526 + M: Jorge Lopez <jorge.lopez2@hp.com> 9527 + L: platform-driver-x86@vger.kernel.org 9528 + S: Maintained 9529 + F: drivers/platform/x86/hp/hp-bioscfg/ 9530 + 9525 9531 HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER 9526 9532 L: platform-driver-x86@vger.kernel.org 9527 9533 S: Orphan ··· 10808 10802 M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 10809 10803 L: platform-driver-x86@vger.kernel.org 10810 10804 S: Maintained 10805 + F: Documentation/ABI/testing/debugfs-tpmi 10811 10806 F: drivers/platform/x86/intel/tpmi.c 10812 10807 F: include/linux/intel_tpmi.h 10813 10808 ··· 19503 19496 F: drivers/media/usb/siano/ 19504 19497 F: drivers/media/usb/siano/ 19505 19498 19499 + SIEMENS IPC LED DRIVERS 19500 + M: Gerd Haeussler <gerd.haeussler.ext@siemens.com> 19501 + M: Xing Tong Wu <xingtong.wu@siemens.com> 19502 + M: Tobias Schaffner <tobias.schaffner@siemens.com> 19503 + L: linux-leds@vger.kernel.org 19504 + S: Maintained 19505 + F: drivers/leds/simple/ 19506 + 19507 + SIEMENS IPC PLATFORM DRIVERS 19508 + M: Gerd Haeussler <gerd.haeussler.ext@siemens.com> 19509 + M: Xing Tong Wu <xingtong.wu@siemens.com> 19510 + M: Tobias Schaffner <tobias.schaffner@siemens.com> 19511 + L: platform-driver-x86@vger.kernel.org 19512 + S: Maintained 19513 + F: drivers/platform/x86/siemens/ 19514 + F: include/linux/platform_data/x86/simatic-ipc-base.h 19515 + F: include/linux/platform_data/x86/simatic-ipc.h 19516 + 19517 + SIEMENS IPC WATCHDOG DRIVERS 19518 + M: Gerd Haeussler <gerd.haeussler.ext@siemens.com> 19519 + M: Xing Tong Wu <xingtong.wu@siemens.com> 19520 + M: Tobias Schaffner <tobias.schaffner@siemens.com> 19521 + L: linux-watchdog@vger.kernel.org 19522 + S: Maintained 19523 + F: drivers/watchdog/simatic-ipc-wdt.c 19524 + 19506 19525 SIFIVE DRIVERS 19507 19526 M: Palmer Dabbelt <palmer@dabbelt.com> 19508 19527 M: Paul Walmsley <paul.walmsley@sifive.com> ··· 23145 23112 F: drivers/net/wireless/legacy/wl3501* 23146 23113 23147 23114 WMI BINARY MOF DRIVER 23148 - L: platform-drivers-x86@vger.kernel.org 23149 - S: Orphan 23115 + M: Armin Wolf <W_Armin@gmx.de> 23116 + R: Thomas Weißschuh <linux@weissschuh.net> 23117 + L: platform-driver-x86@vger.kernel.org 23118 + S: Maintained 23150 23119 F: Documentation/ABI/stable/sysfs-platform-wmi-bmof 23151 23120 F: Documentation/wmi/devices/wmi-bmof.rst 23152 23121 F: drivers/platform/x86/wmi-bmof.c
+1
drivers/leds/simple/Kconfig
··· 2 2 config LEDS_SIEMENS_SIMATIC_IPC 3 3 tristate "LED driver for Siemens Simatic IPCs" 4 4 depends on SIEMENS_SIMATIC_IPC 5 + default y 5 6 help 6 7 This option enables support for the LEDs of several Industrial PCs 7 8 from Siemens.
+1
drivers/platform/chrome/cros_ec.c
··· 12 12 #include <linux/interrupt.h> 13 13 #include <linux/module.h> 14 14 #include <linux/of_platform.h> 15 + #include <linux/platform_device.h> 15 16 #include <linux/platform_data/cros_ec_commands.h> 16 17 #include <linux/platform_data/cros_ec_proto.h> 17 18 #include <linux/slab.h>
+505
drivers/platform/mellanox/mlxbf-bootctl.c
··· 11 11 #include <linux/acpi.h> 12 12 #include <linux/arm-smccc.h> 13 13 #include <linux/delay.h> 14 + #include <linux/if_ether.h> 14 15 #include <linux/iopoll.h> 15 16 #include <linux/module.h> 16 17 #include <linux/platform_device.h> ··· 79 78 /* Rsh log levels. */ 80 79 static const char * const mlxbf_rsh_log_level[] = { 81 80 "INFO", "WARN", "ERR", "ASSERT"}; 81 + 82 + static DEFINE_MUTEX(icm_ops_lock); 83 + static DEFINE_MUTEX(os_up_lock); 84 + static DEFINE_MUTEX(mfg_ops_lock); 85 + 86 + /* 87 + * Objects are stored within the MFG partition per type. 88 + * Type 0 is not supported. 89 + */ 90 + enum { 91 + MLNX_MFG_TYPE_OOB_MAC = 1, 92 + MLNX_MFG_TYPE_OPN_0, 93 + MLNX_MFG_TYPE_OPN_1, 94 + MLNX_MFG_TYPE_OPN_2, 95 + MLNX_MFG_TYPE_SKU_0, 96 + MLNX_MFG_TYPE_SKU_1, 97 + MLNX_MFG_TYPE_SKU_2, 98 + MLNX_MFG_TYPE_MODL_0, 99 + MLNX_MFG_TYPE_MODL_1, 100 + MLNX_MFG_TYPE_MODL_2, 101 + MLNX_MFG_TYPE_SN_0, 102 + MLNX_MFG_TYPE_SN_1, 103 + MLNX_MFG_TYPE_SN_2, 104 + MLNX_MFG_TYPE_UUID_0, 105 + MLNX_MFG_TYPE_UUID_1, 106 + MLNX_MFG_TYPE_UUID_2, 107 + MLNX_MFG_TYPE_UUID_3, 108 + MLNX_MFG_TYPE_UUID_4, 109 + MLNX_MFG_TYPE_REV, 110 + }; 111 + 112 + #define MLNX_MFG_OPN_VAL_LEN 24 113 + #define MLNX_MFG_SKU_VAL_LEN 24 114 + #define MLNX_MFG_MODL_VAL_LEN 24 115 + #define MLNX_MFG_SN_VAL_LEN 24 116 + #define MLNX_MFG_UUID_VAL_LEN 40 117 + #define MLNX_MFG_REV_VAL_LEN 8 118 + #define MLNX_MFG_VAL_QWORD_CNT(type) \ 119 + (MLNX_MFG_##type##_VAL_LEN / sizeof(u64)) 120 + 121 + /* 122 + * The MAC address consists of 6 bytes (2 digits each) separated by ':'. 123 + * The expected format is: "XX:XX:XX:XX:XX:XX" 124 + */ 125 + #define MLNX_MFG_OOB_MAC_FORMAT_LEN \ 126 + ((ETH_ALEN * 2) + (ETH_ALEN - 1)) 82 127 83 128 /* ARM SMC call which is atomic and no need for lock. */ 84 129 static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) ··· 438 391 return count; 439 392 } 440 393 394 + static ssize_t large_icm_show(struct device *dev, 395 + struct device_attribute *attr, char *buf) 396 + { 397 + struct arm_smccc_res res; 398 + 399 + mutex_lock(&icm_ops_lock); 400 + arm_smccc_smc(MLNX_HANDLE_GET_ICM_INFO, 0, 0, 0, 0, 401 + 0, 0, 0, &res); 402 + mutex_unlock(&icm_ops_lock); 403 + if (res.a0) 404 + return -EPERM; 405 + 406 + return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1); 407 + } 408 + 409 + static ssize_t large_icm_store(struct device *dev, 410 + struct device_attribute *attr, 411 + const char *buf, size_t count) 412 + { 413 + struct arm_smccc_res res; 414 + unsigned long icm_data; 415 + int err; 416 + 417 + err = kstrtoul(buf, MLXBF_LARGE_ICMC_MAX_STRING_SIZE, &icm_data); 418 + if (err) 419 + return err; 420 + 421 + if ((icm_data != 0 && icm_data < MLXBF_LARGE_ICMC_SIZE_MIN) || 422 + icm_data > MLXBF_LARGE_ICMC_SIZE_MAX || icm_data % MLXBF_LARGE_ICMC_GRANULARITY) 423 + return -EPERM; 424 + 425 + mutex_lock(&icm_ops_lock); 426 + arm_smccc_smc(MLNX_HANDLE_SET_ICM_INFO, icm_data, 0, 0, 0, 0, 0, 0, &res); 427 + mutex_unlock(&icm_ops_lock); 428 + 429 + return res.a0 ? -EPERM : count; 430 + } 431 + 432 + static ssize_t os_up_store(struct device *dev, 433 + struct device_attribute *attr, 434 + const char *buf, size_t count) 435 + { 436 + struct arm_smccc_res res; 437 + unsigned long val; 438 + int err; 439 + 440 + err = kstrtoul(buf, 10, &val); 441 + if (err) 442 + return err; 443 + 444 + if (val != 1) 445 + return -EINVAL; 446 + 447 + mutex_lock(&os_up_lock); 448 + arm_smccc_smc(MLNX_HANDLE_OS_UP, 0, 0, 0, 0, 0, 0, 0, &res); 449 + mutex_unlock(&os_up_lock); 450 + 451 + return count; 452 + } 453 + 454 + static ssize_t oob_mac_show(struct device *dev, 455 + struct device_attribute *attr, char *buf) 456 + { 457 + struct arm_smccc_res res; 458 + u8 *mac_byte_ptr; 459 + 460 + mutex_lock(&mfg_ops_lock); 461 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 0, 0, 0, 462 + 0, 0, 0, &res); 463 + mutex_unlock(&mfg_ops_lock); 464 + if (res.a0) 465 + return -EPERM; 466 + 467 + mac_byte_ptr = (u8 *)&res.a1; 468 + 469 + return sysfs_format_mac(buf, mac_byte_ptr, ETH_ALEN); 470 + } 471 + 472 + static ssize_t oob_mac_store(struct device *dev, 473 + struct device_attribute *attr, 474 + const char *buf, size_t count) 475 + { 476 + unsigned int byte[MLNX_MFG_OOB_MAC_FORMAT_LEN] = { 0 }; 477 + struct arm_smccc_res res; 478 + int byte_idx, len; 479 + u64 mac_addr = 0; 480 + u8 *mac_byte_ptr; 481 + 482 + if ((count - 1) != MLNX_MFG_OOB_MAC_FORMAT_LEN) 483 + return -EINVAL; 484 + 485 + len = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 486 + &byte[0], &byte[1], &byte[2], 487 + &byte[3], &byte[4], &byte[5]); 488 + if (len != ETH_ALEN) 489 + return -EINVAL; 490 + 491 + mac_byte_ptr = (u8 *)&mac_addr; 492 + 493 + for (byte_idx = 0; byte_idx < ETH_ALEN; byte_idx++) 494 + mac_byte_ptr[byte_idx] = (u8)byte[byte_idx]; 495 + 496 + mutex_lock(&mfg_ops_lock); 497 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 498 + ETH_ALEN, mac_addr, 0, 0, 0, 0, &res); 499 + mutex_unlock(&mfg_ops_lock); 500 + 501 + return res.a0 ? -EPERM : count; 502 + } 503 + 504 + static ssize_t opn_show(struct device *dev, 505 + struct device_attribute *attr, char *buf) 506 + { 507 + u64 opn_data[MLNX_MFG_VAL_QWORD_CNT(OPN) + 1] = { 0 }; 508 + struct arm_smccc_res res; 509 + int word; 510 + 511 + mutex_lock(&mfg_ops_lock); 512 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) { 513 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 514 + MLNX_MFG_TYPE_OPN_0 + word, 515 + 0, 0, 0, 0, 0, 0, &res); 516 + if (res.a0) { 517 + mutex_unlock(&mfg_ops_lock); 518 + return -EPERM; 519 + } 520 + opn_data[word] = res.a1; 521 + } 522 + mutex_unlock(&mfg_ops_lock); 523 + 524 + return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data); 525 + } 526 + 527 + static ssize_t opn_store(struct device *dev, 528 + struct device_attribute *attr, 529 + const char *buf, size_t count) 530 + { 531 + u64 opn[MLNX_MFG_VAL_QWORD_CNT(OPN)] = { 0 }; 532 + struct arm_smccc_res res; 533 + int word; 534 + 535 + if (count > MLNX_MFG_OPN_VAL_LEN) 536 + return -EINVAL; 537 + 538 + memcpy(opn, buf, count); 539 + 540 + mutex_lock(&mfg_ops_lock); 541 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) { 542 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 543 + MLNX_MFG_TYPE_OPN_0 + word, 544 + sizeof(u64), opn[word], 0, 0, 0, 0, &res); 545 + if (res.a0) { 546 + mutex_unlock(&mfg_ops_lock); 547 + return -EPERM; 548 + } 549 + } 550 + mutex_unlock(&mfg_ops_lock); 551 + 552 + return count; 553 + } 554 + 555 + static ssize_t sku_show(struct device *dev, 556 + struct device_attribute *attr, char *buf) 557 + { 558 + u64 sku_data[MLNX_MFG_VAL_QWORD_CNT(SKU) + 1] = { 0 }; 559 + struct arm_smccc_res res; 560 + int word; 561 + 562 + mutex_lock(&mfg_ops_lock); 563 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) { 564 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 565 + MLNX_MFG_TYPE_SKU_0 + word, 566 + 0, 0, 0, 0, 0, 0, &res); 567 + if (res.a0) { 568 + mutex_unlock(&mfg_ops_lock); 569 + return -EPERM; 570 + } 571 + sku_data[word] = res.a1; 572 + } 573 + mutex_unlock(&mfg_ops_lock); 574 + 575 + return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data); 576 + } 577 + 578 + static ssize_t sku_store(struct device *dev, 579 + struct device_attribute *attr, 580 + const char *buf, size_t count) 581 + { 582 + u64 sku[MLNX_MFG_VAL_QWORD_CNT(SKU)] = { 0 }; 583 + struct arm_smccc_res res; 584 + int word; 585 + 586 + if (count > MLNX_MFG_SKU_VAL_LEN) 587 + return -EINVAL; 588 + 589 + memcpy(sku, buf, count); 590 + 591 + mutex_lock(&mfg_ops_lock); 592 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) { 593 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 594 + MLNX_MFG_TYPE_SKU_0 + word, 595 + sizeof(u64), sku[word], 0, 0, 0, 0, &res); 596 + if (res.a0) { 597 + mutex_unlock(&mfg_ops_lock); 598 + return -EPERM; 599 + } 600 + } 601 + mutex_unlock(&mfg_ops_lock); 602 + 603 + return count; 604 + } 605 + 606 + static ssize_t modl_show(struct device *dev, 607 + struct device_attribute *attr, char *buf) 608 + { 609 + u64 modl_data[MLNX_MFG_VAL_QWORD_CNT(MODL) + 1] = { 0 }; 610 + struct arm_smccc_res res; 611 + int word; 612 + 613 + mutex_lock(&mfg_ops_lock); 614 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) { 615 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 616 + MLNX_MFG_TYPE_MODL_0 + word, 617 + 0, 0, 0, 0, 0, 0, &res); 618 + if (res.a0) { 619 + mutex_unlock(&mfg_ops_lock); 620 + return -EPERM; 621 + } 622 + modl_data[word] = res.a1; 623 + } 624 + mutex_unlock(&mfg_ops_lock); 625 + 626 + return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data); 627 + } 628 + 629 + static ssize_t modl_store(struct device *dev, 630 + struct device_attribute *attr, 631 + const char *buf, size_t count) 632 + { 633 + u64 modl[MLNX_MFG_VAL_QWORD_CNT(MODL)] = { 0 }; 634 + struct arm_smccc_res res; 635 + int word; 636 + 637 + if (count > MLNX_MFG_MODL_VAL_LEN) 638 + return -EINVAL; 639 + 640 + memcpy(modl, buf, count); 641 + 642 + mutex_lock(&mfg_ops_lock); 643 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) { 644 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 645 + MLNX_MFG_TYPE_MODL_0 + word, 646 + sizeof(u64), modl[word], 0, 0, 0, 0, &res); 647 + if (res.a0) { 648 + mutex_unlock(&mfg_ops_lock); 649 + return -EPERM; 650 + } 651 + } 652 + mutex_unlock(&mfg_ops_lock); 653 + 654 + return count; 655 + } 656 + 657 + static ssize_t sn_show(struct device *dev, 658 + struct device_attribute *attr, char *buf) 659 + { 660 + u64 sn_data[MLNX_MFG_VAL_QWORD_CNT(SN) + 1] = { 0 }; 661 + struct arm_smccc_res res; 662 + int word; 663 + 664 + mutex_lock(&mfg_ops_lock); 665 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) { 666 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 667 + MLNX_MFG_TYPE_SN_0 + word, 668 + 0, 0, 0, 0, 0, 0, &res); 669 + if (res.a0) { 670 + mutex_unlock(&mfg_ops_lock); 671 + return -EPERM; 672 + } 673 + sn_data[word] = res.a1; 674 + } 675 + mutex_unlock(&mfg_ops_lock); 676 + 677 + return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data); 678 + } 679 + 680 + static ssize_t sn_store(struct device *dev, 681 + struct device_attribute *attr, 682 + const char *buf, size_t count) 683 + { 684 + u64 sn[MLNX_MFG_VAL_QWORD_CNT(SN)] = { 0 }; 685 + struct arm_smccc_res res; 686 + int word; 687 + 688 + if (count > MLNX_MFG_SN_VAL_LEN) 689 + return -EINVAL; 690 + 691 + memcpy(sn, buf, count); 692 + 693 + mutex_lock(&mfg_ops_lock); 694 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) { 695 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 696 + MLNX_MFG_TYPE_SN_0 + word, 697 + sizeof(u64), sn[word], 0, 0, 0, 0, &res); 698 + if (res.a0) { 699 + mutex_unlock(&mfg_ops_lock); 700 + return -EPERM; 701 + } 702 + } 703 + mutex_unlock(&mfg_ops_lock); 704 + 705 + return count; 706 + } 707 + 708 + static ssize_t uuid_show(struct device *dev, 709 + struct device_attribute *attr, char *buf) 710 + { 711 + u64 uuid_data[MLNX_MFG_VAL_QWORD_CNT(UUID) + 1] = { 0 }; 712 + struct arm_smccc_res res; 713 + int word; 714 + 715 + mutex_lock(&mfg_ops_lock); 716 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) { 717 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 718 + MLNX_MFG_TYPE_UUID_0 + word, 719 + 0, 0, 0, 0, 0, 0, &res); 720 + if (res.a0) { 721 + mutex_unlock(&mfg_ops_lock); 722 + return -EPERM; 723 + } 724 + uuid_data[word] = res.a1; 725 + } 726 + mutex_unlock(&mfg_ops_lock); 727 + 728 + return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data); 729 + } 730 + 731 + static ssize_t uuid_store(struct device *dev, 732 + struct device_attribute *attr, 733 + const char *buf, size_t count) 734 + { 735 + u64 uuid[MLNX_MFG_VAL_QWORD_CNT(UUID)] = { 0 }; 736 + struct arm_smccc_res res; 737 + int word; 738 + 739 + if (count > MLNX_MFG_UUID_VAL_LEN) 740 + return -EINVAL; 741 + 742 + memcpy(uuid, buf, count); 743 + 744 + mutex_lock(&mfg_ops_lock); 745 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) { 746 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 747 + MLNX_MFG_TYPE_UUID_0 + word, 748 + sizeof(u64), uuid[word], 0, 0, 0, 0, &res); 749 + if (res.a0) { 750 + mutex_unlock(&mfg_ops_lock); 751 + return -EPERM; 752 + } 753 + } 754 + mutex_unlock(&mfg_ops_lock); 755 + 756 + return count; 757 + } 758 + 759 + static ssize_t rev_show(struct device *dev, 760 + struct device_attribute *attr, char *buf) 761 + { 762 + u64 rev_data[MLNX_MFG_VAL_QWORD_CNT(REV) + 1] = { 0 }; 763 + struct arm_smccc_res res; 764 + int word; 765 + 766 + mutex_lock(&mfg_ops_lock); 767 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) { 768 + arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 769 + MLNX_MFG_TYPE_REV + word, 770 + 0, 0, 0, 0, 0, 0, &res); 771 + if (res.a0) { 772 + mutex_unlock(&mfg_ops_lock); 773 + return -EPERM; 774 + } 775 + rev_data[word] = res.a1; 776 + } 777 + mutex_unlock(&mfg_ops_lock); 778 + 779 + return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data); 780 + } 781 + 782 + static ssize_t rev_store(struct device *dev, 783 + struct device_attribute *attr, 784 + const char *buf, size_t count) 785 + { 786 + u64 rev[MLNX_MFG_VAL_QWORD_CNT(REV)] = { 0 }; 787 + struct arm_smccc_res res; 788 + int word; 789 + 790 + if (count > MLNX_MFG_REV_VAL_LEN) 791 + return -EINVAL; 792 + 793 + memcpy(rev, buf, count); 794 + 795 + mutex_lock(&mfg_ops_lock); 796 + for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) { 797 + arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 798 + MLNX_MFG_TYPE_REV + word, 799 + sizeof(u64), rev[word], 0, 0, 0, 0, &res); 800 + if (res.a0) { 801 + mutex_unlock(&mfg_ops_lock); 802 + return -EPERM; 803 + } 804 + } 805 + mutex_unlock(&mfg_ops_lock); 806 + 807 + return count; 808 + } 809 + 810 + static ssize_t mfg_lock_store(struct device *dev, 811 + struct device_attribute *attr, 812 + const char *buf, size_t count) 813 + { 814 + struct arm_smccc_res res; 815 + unsigned long val; 816 + int err; 817 + 818 + err = kstrtoul(buf, 10, &val); 819 + if (err) 820 + return err; 821 + 822 + if (val != 1) 823 + return -EINVAL; 824 + 825 + mutex_lock(&mfg_ops_lock); 826 + arm_smccc_smc(MLXBF_BOOTCTL_LOCK_MFG_INFO, 0, 0, 0, 0, 0, 0, 0, &res); 827 + mutex_unlock(&mfg_ops_lock); 828 + 829 + return count; 830 + } 831 + 441 832 static DEVICE_ATTR_RW(post_reset_wdog); 442 833 static DEVICE_ATTR_RW(reset_action); 443 834 static DEVICE_ATTR_RW(second_reset_action); ··· 883 398 static DEVICE_ATTR_RO(secure_boot_fuse_state); 884 399 static DEVICE_ATTR_WO(fw_reset); 885 400 static DEVICE_ATTR_WO(rsh_log); 401 + static DEVICE_ATTR_RW(large_icm); 402 + static DEVICE_ATTR_WO(os_up); 403 + static DEVICE_ATTR_RW(oob_mac); 404 + static DEVICE_ATTR_RW(opn); 405 + static DEVICE_ATTR_RW(sku); 406 + static DEVICE_ATTR_RW(modl); 407 + static DEVICE_ATTR_RW(sn); 408 + static DEVICE_ATTR_RW(uuid); 409 + static DEVICE_ATTR_RW(rev); 410 + static DEVICE_ATTR_WO(mfg_lock); 886 411 887 412 static struct attribute *mlxbf_bootctl_attrs[] = { 888 413 &dev_attr_post_reset_wdog.attr, ··· 902 407 &dev_attr_secure_boot_fuse_state.attr, 903 408 &dev_attr_fw_reset.attr, 904 409 &dev_attr_rsh_log.attr, 410 + &dev_attr_large_icm.attr, 411 + &dev_attr_os_up.attr, 412 + &dev_attr_oob_mac.attr, 413 + &dev_attr_opn.attr, 414 + &dev_attr_sku.attr, 415 + &dev_attr_modl.attr, 416 + &dev_attr_sn.attr, 417 + &dev_attr_uuid.attr, 418 + &dev_attr_rev.attr, 419 + &dev_attr_mfg_lock.attr, 905 420 NULL 906 421 }; 907 422
+27
drivers/platform/mellanox/mlxbf-bootctl.h
··· 81 81 */ 82 82 #define MLXBF_BOOTCTL_FW_RESET 0x8200000D 83 83 84 + /* 85 + * SMC function IDs to set, get and lock the manufacturing information 86 + * stored within the eeprom. 87 + */ 88 + #define MLXBF_BOOTCTL_SET_MFG_INFO 0x8200000E 89 + #define MLXBF_BOOTCTL_GET_MFG_INFO 0x8200000F 90 + #define MLXBF_BOOTCTL_LOCK_MFG_INFO 0x82000011 91 + 92 + /* 93 + * SMC function IDs to set and get the large ICM carveout size 94 + * stored in the eeprom. 95 + */ 96 + #define MLNX_HANDLE_SET_ICM_INFO 0x82000012 97 + #define MLNX_HANDLE_GET_ICM_INFO 0x82000013 98 + 99 + #define MAX_ICM_BUFFER_SIZE 10 100 + 101 + /* 102 + * SMC function ID to set the ARM boot state to up 103 + */ 104 + #define MLNX_HANDLE_OS_UP 0x82000014 105 + 84 106 /* SMC function IDs for SiP Service queries */ 85 107 #define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00 86 108 #define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01 ··· 127 105 128 106 /* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */ 129 107 #define MLXBF_BOOTCTL_NONE 0x7fffffff /* Don't change next boot action */ 108 + 109 + #define MLXBF_LARGE_ICMC_MAX_STRING_SIZE 16 110 + #define MLXBF_LARGE_ICMC_SIZE_MIN 0x80 111 + #define MLXBF_LARGE_ICMC_SIZE_MAX 0x100000 112 + #define MLXBF_LARGE_ICMC_GRANULARITY MLXBF_LARGE_ICMC_SIZE_MIN 130 113 131 114 #endif /* __MLXBF_BOOTCTL_H__ */
+1 -2
drivers/platform/mellanox/mlxreg-hotplug.c
··· 12 12 #include <linux/i2c.h> 13 13 #include <linux/interrupt.h> 14 14 #include <linux/module.h> 15 - #include <linux/of_device.h> 16 15 #include <linux/platform_data/mlxreg.h> 17 16 #include <linux/platform_device.h> 18 17 #include <linux/spinlock.h> ··· 112 113 * Return if adapter number is negative. It could be in case hotplug 113 114 * event is not associated with hotplug device. 114 115 */ 115 - if (data->hpdev.nr < 0) 116 + if (data->hpdev.nr < 0 && data->hpdev.action != MLXREG_HOTPLUG_DEVICE_NO_ACTION) 116 117 return 0; 117 118 118 119 pdata = dev_get_platdata(&priv->pdev->dev);
-1
drivers/platform/mellanox/mlxreg-io.c
··· 11 11 #include <linux/hwmon.h> 12 12 #include <linux/hwmon-sysfs.h> 13 13 #include <linux/module.h> 14 - #include <linux/of_device.h> 15 14 #include <linux/platform_data/mlxreg.h> 16 15 #include <linux/platform_device.h> 17 16 #include <linux/regmap.h>
+8 -4
drivers/platform/mellanox/nvsw-sn2201.c
··· 84 84 #define NVSW_SN2201_MAIN_MUX_CH5_NR (NVSW_SN2201_MAIN_MUX_CH0_NR + 5) 85 85 #define NVSW_SN2201_MAIN_MUX_CH6_NR (NVSW_SN2201_MAIN_MUX_CH0_NR + 6) 86 86 #define NVSW_SN2201_MAIN_MUX_CH7_NR (NVSW_SN2201_MAIN_MUX_CH0_NR + 7) 87 + #define NVSW_SN2201_2ND_MUX_CH0_NR (NVSW_SN2201_MAIN_MUX_CH7_NR + 1) 88 + #define NVSW_SN2201_2ND_MUX_CH1_NR (NVSW_SN2201_MAIN_MUX_CH7_NR + 2) 89 + #define NVSW_SN2201_2ND_MUX_CH2_NR (NVSW_SN2201_MAIN_MUX_CH7_NR + 3) 90 + #define NVSW_SN2201_2ND_MUX_CH3_NR (NVSW_SN2201_MAIN_MUX_CH7_NR + 4) 87 91 88 92 #define NVSW_SN2201_CPLD_NR NVSW_SN2201_MAIN_MUX_CH0_NR 89 93 #define NVSW_SN2201_NR_NONE -1 ··· 429 425 .reg = NVSW_SN2201_FAN_PRSNT_STATUS_OFFSET, 430 426 .mask = BIT(0), 431 427 .hpdev.brdinfo = &nvsw_sn2201_fan_devices[0], 432 - .hpdev.nr = NVSW_SN2201_NR_NONE, 428 + .hpdev.nr = NVSW_SN2201_2ND_MUX_CH0_NR, 433 429 }, 434 430 { 435 431 .label = "fan2", 436 432 .reg = NVSW_SN2201_FAN_PRSNT_STATUS_OFFSET, 437 433 .mask = BIT(1), 438 434 .hpdev.brdinfo = &nvsw_sn2201_fan_devices[1], 439 - .hpdev.nr = NVSW_SN2201_NR_NONE, 435 + .hpdev.nr = NVSW_SN2201_2ND_MUX_CH1_NR, 440 436 }, 441 437 { 442 438 .label = "fan3", 443 439 .reg = NVSW_SN2201_FAN_PRSNT_STATUS_OFFSET, 444 440 .mask = BIT(2), 445 441 .hpdev.brdinfo = &nvsw_sn2201_fan_devices[2], 446 - .hpdev.nr = NVSW_SN2201_NR_NONE, 442 + .hpdev.nr = NVSW_SN2201_2ND_MUX_CH2_NR, 447 443 }, 448 444 { 449 445 .label = "fan4", 450 446 .reg = NVSW_SN2201_FAN_PRSNT_STATUS_OFFSET, 451 447 .mask = BIT(3), 452 448 .hpdev.brdinfo = &nvsw_sn2201_fan_devices[3], 453 - .hpdev.nr = NVSW_SN2201_NR_NONE, 449 + .hpdev.nr = NVSW_SN2201_2ND_MUX_CH3_NR, 454 450 }, 455 451 }; 456 452
+18 -13
drivers/platform/x86/Kconfig
··· 965 965 966 966 config MLX_PLATFORM 967 967 tristate "Mellanox Technologies platform support" 968 - depends on I2C 968 + depends on ACPI && I2C && PCI 969 969 select REGMAP 970 970 help 971 971 This option enables system support for the Mellanox Technologies ··· 1074 1074 low level access for debug work and updating the firmware. Say 1075 1075 N unless you will be doing this on an Intel MID platform. 1076 1076 1077 - config SIEMENS_SIMATIC_IPC 1078 - tristate "Siemens Simatic IPC Class driver" 1079 - depends on PCI 1080 - help 1081 - This Simatic IPC class driver is the central of several drivers. It 1082 - is mainly used for system identification, after which drivers in other 1083 - classes will take care of driving specifics of those machines. 1084 - i.e. LEDs and watchdog. 1085 - 1086 - To compile this driver as a module, choose M here: the module 1087 - will be called simatic-ipc. 1077 + source "drivers/platform/x86/siemens/Kconfig" 1088 1078 1089 1079 config WINMATE_FM07_KEYS 1090 1080 tristate "Winmate FM07/FM07P front-panel keys driver" ··· 1084 1094 buttons below the display. This module adds an input device 1085 1095 that delivers key events when these buttons are pressed. 1086 1096 1097 + config SEL3350_PLATFORM 1098 + tristate "SEL-3350 LEDs and power supplies" 1099 + depends on ACPI 1100 + depends on GPIOLIB 1101 + depends on PINCTRL_BROXTON 1102 + select POWER_SUPPLY 1103 + select NEW_LEDS 1104 + select LEDS_CLASS 1105 + select LEDS_GPIO 1106 + help 1107 + Support for LEDs and power supplies on SEL-3350 computers. 1108 + 1109 + To compile this driver as a module, choose M here: the module 1110 + will be called sel3350-platform. 1111 + 1087 1112 endif # X86_PLATFORM_DEVICES 1088 1113 1089 1114 config P2SB 1090 - bool "Primary to Sideband (P2SB) bridge access support" 1115 + bool 1091 1116 depends on PCI && X86 1092 1117 help 1093 1118 The Primary to Sideband (P2SB) bridge is an interface to some
+4 -1
drivers/platform/x86/Makefile
··· 131 131 obj-$(CONFIG_X86_INTEL_LPSS) += pmc_atom.o 132 132 133 133 # Siemens Simatic Industrial PCs 134 - obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o 134 + obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += siemens/ 135 135 136 136 # Winmate 137 137 obj-$(CONFIG_WINMATE_FM07_KEYS) += winmate-fm07-keys.o 138 + 139 + # SEL 140 + obj-$(CONFIG_SEL3350_PLATFORM) += sel3350-platform.o
+1 -15
drivers/platform/x86/amd/Kconfig
··· 4 4 # 5 5 6 6 source "drivers/platform/x86/amd/pmf/Kconfig" 7 - 8 - config AMD_PMC 9 - tristate "AMD SoC PMC driver" 10 - depends on ACPI && PCI && RTC_CLASS && AMD_NB 11 - select SERIO 12 - help 13 - The driver provides support for AMD Power Management Controller 14 - primarily responsible for S2Idle transactions that are driven from 15 - a platform firmware running on SMU. This driver also provides a debug 16 - mechanism to investigate the S2Idle transactions and failures. 17 - 18 - Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. 19 - 20 - If you choose to compile this driver as a module the module will be 21 - called amd-pmc. 7 + source "drivers/platform/x86/amd/pmc/Kconfig" 22 8 23 9 config AMD_HSMP 24 10 tristate "AMD HSMP Driver"
+1 -2
drivers/platform/x86/amd/Makefile
··· 4 4 # AMD x86 Platform-Specific Drivers 5 5 # 6 6 7 - amd-pmc-y := pmc.o pmc-quirks.o 8 - obj-$(CONFIG_AMD_PMC) += amd-pmc.o 7 + obj-$(CONFIG_AMD_PMC) += pmc/ 9 8 amd_hsmp-y := hsmp.o 10 9 obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o 11 10 obj-$(CONFIG_AMD_PMF) += pmf/
drivers/platform/x86/amd/pmc-quirks.c drivers/platform/x86/amd/pmc/pmc-quirks.c
drivers/platform/x86/amd/pmc.c drivers/platform/x86/amd/pmc/pmc.c
drivers/platform/x86/amd/pmc.h drivers/platform/x86/amd/pmc/pmc.h
+20
drivers/platform/x86/amd/pmc/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # AMD PMC Driver 4 + # 5 + 6 + config AMD_PMC 7 + tristate "AMD SoC PMC driver" 8 + depends on ACPI && PCI && RTC_CLASS && AMD_NB 9 + depends on SUSPEND 10 + select SERIO 11 + help 12 + The driver provides support for AMD Power Management Controller 13 + primarily responsible for S2Idle transactions that are driven from 14 + a platform firmware running on SMU. This driver also provides a debug 15 + mechanism to investigate the S2Idle transactions and failures. 16 + 17 + Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. 18 + 19 + If you choose to compile this driver as a module the module will be 20 + called amd-pmc.
+8
drivers/platform/x86/amd/pmc/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for linux/drivers/platform/x86/amd/pmc 4 + # AMD Power Management Controller Driver 5 + # 6 + 7 + amd-pmc-objs := pmc.o pmc-quirks.o 8 + obj-$(CONFIG_AMD_PMC) += amd-pmc.o
+3 -2
drivers/platform/x86/amd/pmf/cnqf.c
··· 8 8 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> 9 9 */ 10 10 11 + #include <linux/string_choices.h> 11 12 #include <linux/workqueue.h> 12 13 #include "pmf.h" 13 14 ··· 400 399 amd_pmf_set_sps_power_limits(pdev); 401 400 } 402 401 403 - dev_dbg(pdev->dev, "Received CnQF %s\n", input ? "on" : "off"); 402 + dev_dbg(pdev->dev, "Received CnQF %s\n", str_on_off(input)); 404 403 return count; 405 404 } 406 405 ··· 410 409 { 411 410 struct amd_pmf_dev *pdev = dev_get_drvdata(dev); 412 411 413 - return sysfs_emit(buf, "%s\n", pdev->cnqf_enabled ? "on" : "off"); 412 + return sysfs_emit(buf, "%s\n", str_on_off(pdev->cnqf_enabled)); 414 413 } 415 414 416 415 static DEVICE_ATTR_RW(cnqf_enable);
+2 -1
drivers/platform/x86/amd/pmf/core.c
··· 324 324 325 325 static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) 326 326 { 327 - if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { 327 + if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) || 328 + is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) { 328 329 power_supply_unreg_notifier(&dev->pwr_src_notifier); 329 330 amd_pmf_deinit_sps(dev); 330 331 }
+622 -24
drivers/platform/x86/asus-wmi.c
··· 72 72 73 73 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) 74 74 75 + #define ASUS_MID_FAN_DESC "mid_fan" 75 76 #define ASUS_GPU_FAN_DESC "gpu_fan" 76 77 #define ASUS_FAN_DESC "cpu_fan" 77 78 #define ASUS_FAN_MFUN 0x13 ··· 113 112 #define FAN_CURVE_BUF_LEN 32 114 113 #define FAN_CURVE_DEV_CPU 0x00 115 114 #define FAN_CURVE_DEV_GPU 0x01 115 + #define FAN_CURVE_DEV_MID 0x02 116 116 /* Mask to determine if setting temperature or percentage */ 117 117 #define FAN_CURVE_PWM_MASK 0x04 118 + 119 + /* Limits for tunables available on ASUS ROG laptops */ 120 + #define PPT_TOTAL_MIN 5 121 + #define PPT_TOTAL_MAX 250 122 + #define PPT_CPU_MIN 5 123 + #define PPT_CPU_MAX 130 124 + #define NVIDIA_BOOST_MIN 5 125 + #define NVIDIA_BOOST_MAX 25 126 + #define NVIDIA_TEMP_MIN 75 127 + #define NVIDIA_TEMP_MAX 87 118 128 119 129 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; 120 130 ··· 241 229 242 230 enum fan_type fan_type; 243 231 enum fan_type gpu_fan_type; 232 + enum fan_type mid_fan_type; 244 233 int fan_pwm_mode; 245 234 int gpu_fan_pwm_mode; 235 + int mid_fan_pwm_mode; 246 236 int agfn_pwm; 247 237 248 238 bool fan_boost_mode_available; 249 239 u8 fan_boost_mode_mask; 250 240 u8 fan_boost_mode; 251 241 242 + bool charge_mode_available; 252 243 bool egpu_enable_available; 244 + bool egpu_connect_available; 253 245 bool dgpu_disable_available; 254 246 bool gpu_mux_mode_available; 247 + 248 + /* Tunables provided by ASUS for gaming laptops */ 249 + bool ppt_pl2_sppt_available; 250 + bool ppt_pl1_spl_available; 251 + bool ppt_apu_sppt_available; 252 + bool ppt_plat_sppt_available; 253 + bool ppt_fppt_available; 254 + bool nv_dyn_boost_available; 255 + bool nv_temp_tgt_available; 255 256 256 257 bool kbd_rgb_mode_available; 257 258 bool kbd_rgb_state_available; ··· 274 249 275 250 bool cpu_fan_curve_available; 276 251 bool gpu_fan_curve_available; 277 - struct fan_curve_data custom_fan_curves[2]; 252 + bool mid_fan_curve_available; 253 + struct fan_curve_data custom_fan_curves[3]; 278 254 279 255 struct platform_profile_handler platform_profile_handler; 280 256 bool platform_profile_support; ··· 284 258 bool battery_rsoc_available; 285 259 286 260 bool panel_overdrive_available; 261 + bool mini_led_mode_available; 287 262 288 263 struct hotplug_slot hotplug_slot; 289 264 struct mutex hotplug_lock; ··· 613 586 asus_wmi_tablet_sw_report(asus, result); 614 587 } 615 588 589 + /* Charging mode, 1=Barrel, 2=USB ******************************************/ 590 + static ssize_t charge_mode_show(struct device *dev, 591 + struct device_attribute *attr, char *buf) 592 + { 593 + struct asus_wmi *asus = dev_get_drvdata(dev); 594 + int result, value; 595 + 596 + result = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CHARGE_MODE, &value); 597 + if (result < 0) 598 + return result; 599 + 600 + return sysfs_emit(buf, "%d\n", value & 0xff); 601 + } 602 + 603 + static DEVICE_ATTR_RO(charge_mode); 604 + 616 605 /* dGPU ********************************************************************/ 617 606 static ssize_t dgpu_disable_show(struct device *dev, 618 607 struct device_attribute *attr, char *buf) ··· 664 621 665 622 if (disable > 1) 666 623 return -EINVAL; 624 + 625 + if (asus->gpu_mux_mode_available) { 626 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX); 627 + if (result < 0) 628 + /* An error here may signal greater failure of GPU handling */ 629 + return result; 630 + if (!result && disable) { 631 + err = -ENODEV; 632 + pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err); 633 + return err; 634 + } 635 + } 667 636 668 637 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result); 669 638 if (err) { ··· 725 670 if (enable > 1) 726 671 return -EINVAL; 727 672 673 + err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU_CONNECTED); 674 + if (err < 0) { 675 + pr_warn("Failed to get egpu connection status: %d\n", err); 676 + return err; 677 + } 678 + 679 + if (asus->gpu_mux_mode_available) { 680 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX); 681 + if (result < 0) { 682 + /* An error here may signal greater failure of GPU handling */ 683 + pr_warn("Failed to get gpu mux status: %d\n", result); 684 + return result; 685 + } 686 + if (!result && enable) { 687 + err = -ENODEV; 688 + pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err); 689 + return err; 690 + } 691 + } 692 + 728 693 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result); 729 694 if (err) { 730 - pr_warn("Failed to set egpu disable: %d\n", err); 695 + pr_warn("Failed to set egpu state: %d\n", err); 731 696 return err; 732 697 } 733 698 734 699 if (result > 1) { 735 - pr_warn("Failed to set egpu disable (retval): 0x%x\n", result); 700 + pr_warn("Failed to set egpu state (retval): 0x%x\n", result); 736 701 return -EIO; 737 702 } 738 703 ··· 761 686 return count; 762 687 } 763 688 static DEVICE_ATTR_RW(egpu_enable); 689 + 690 + /* Is eGPU connected? *********************************************************/ 691 + static ssize_t egpu_connected_show(struct device *dev, 692 + struct device_attribute *attr, char *buf) 693 + { 694 + struct asus_wmi *asus = dev_get_drvdata(dev); 695 + int result; 696 + 697 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU_CONNECTED); 698 + if (result < 0) 699 + return result; 700 + 701 + return sysfs_emit(buf, "%d\n", result); 702 + } 703 + 704 + static DEVICE_ATTR_RO(egpu_connected); 764 705 765 706 /* gpu mux switch *************************************************************/ 766 707 static ssize_t gpu_mux_mode_show(struct device *dev, ··· 806 715 807 716 if (optimus > 1) 808 717 return -EINVAL; 718 + 719 + if (asus->dgpu_disable_available) { 720 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU); 721 + if (result < 0) 722 + /* An error here may signal greater failure of GPU handling */ 723 + return result; 724 + if (result && !optimus) { 725 + err = -ENODEV; 726 + pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err); 727 + return err; 728 + } 729 + } 730 + 731 + if (asus->egpu_enable_available) { 732 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU); 733 + if (result < 0) 734 + /* An error here may signal greater failure of GPU handling */ 735 + return result; 736 + if (result && !optimus) { 737 + err = -ENODEV; 738 + pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err); 739 + return err; 740 + } 741 + } 809 742 810 743 err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, optimus, &result); 811 744 if (err) { ··· 973 858 NULL, 974 859 NULL, 975 860 }; 861 + 862 + /* Tunable: PPT: Intel=PL1, AMD=SPPT *****************************************/ 863 + static ssize_t ppt_pl2_sppt_store(struct device *dev, 864 + struct device_attribute *attr, 865 + const char *buf, size_t count) 866 + { 867 + int result, err; 868 + u32 value; 869 + 870 + struct asus_wmi *asus = dev_get_drvdata(dev); 871 + 872 + result = kstrtou32(buf, 10, &value); 873 + if (result) 874 + return result; 875 + 876 + if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX) 877 + return -EINVAL; 878 + 879 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL2_SPPT, value, &result); 880 + if (err) { 881 + pr_warn("Failed to set ppt_pl2_sppt: %d\n", err); 882 + return err; 883 + } 884 + 885 + if (result > 1) { 886 + pr_warn("Failed to set ppt_pl2_sppt (result): 0x%x\n", result); 887 + return -EIO; 888 + } 889 + 890 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl2_sppt"); 891 + 892 + return count; 893 + } 894 + static DEVICE_ATTR_WO(ppt_pl2_sppt); 895 + 896 + /* Tunable: PPT, Intel=PL1, AMD=SPL ******************************************/ 897 + static ssize_t ppt_pl1_spl_store(struct device *dev, 898 + struct device_attribute *attr, 899 + const char *buf, size_t count) 900 + { 901 + int result, err; 902 + u32 value; 903 + 904 + struct asus_wmi *asus = dev_get_drvdata(dev); 905 + 906 + result = kstrtou32(buf, 10, &value); 907 + if (result) 908 + return result; 909 + 910 + if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX) 911 + return -EINVAL; 912 + 913 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PL1_SPL, value, &result); 914 + if (err) { 915 + pr_warn("Failed to set ppt_pl1_spl: %d\n", err); 916 + return err; 917 + } 918 + 919 + if (result > 1) { 920 + pr_warn("Failed to set ppt_pl1_spl (result): 0x%x\n", result); 921 + return -EIO; 922 + } 923 + 924 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl1_spl"); 925 + 926 + return count; 927 + } 928 + static DEVICE_ATTR_WO(ppt_pl1_spl); 929 + 930 + /* Tunable: PPT APU FPPT ******************************************************/ 931 + static ssize_t ppt_fppt_store(struct device *dev, 932 + struct device_attribute *attr, 933 + const char *buf, size_t count) 934 + { 935 + int result, err; 936 + u32 value; 937 + 938 + struct asus_wmi *asus = dev_get_drvdata(dev); 939 + 940 + result = kstrtou32(buf, 10, &value); 941 + if (result) 942 + return result; 943 + 944 + if (value < PPT_TOTAL_MIN || value > PPT_TOTAL_MAX) 945 + return -EINVAL; 946 + 947 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_FPPT, value, &result); 948 + if (err) { 949 + pr_warn("Failed to set ppt_fppt: %d\n", err); 950 + return err; 951 + } 952 + 953 + if (result > 1) { 954 + pr_warn("Failed to set ppt_fppt (result): 0x%x\n", result); 955 + return -EIO; 956 + } 957 + 958 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_fpu_sppt"); 959 + 960 + return count; 961 + } 962 + static DEVICE_ATTR_WO(ppt_fppt); 963 + 964 + /* Tunable: PPT APU SPPT *****************************************************/ 965 + static ssize_t ppt_apu_sppt_store(struct device *dev, 966 + struct device_attribute *attr, 967 + const char *buf, size_t count) 968 + { 969 + int result, err; 970 + u32 value; 971 + 972 + struct asus_wmi *asus = dev_get_drvdata(dev); 973 + 974 + result = kstrtou32(buf, 10, &value); 975 + if (result) 976 + return result; 977 + 978 + if (value < PPT_CPU_MIN || value > PPT_CPU_MAX) 979 + return -EINVAL; 980 + 981 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_APU_SPPT, value, &result); 982 + if (err) { 983 + pr_warn("Failed to set ppt_apu_sppt: %d\n", err); 984 + return err; 985 + } 986 + 987 + if (result > 1) { 988 + pr_warn("Failed to set ppt_apu_sppt (result): 0x%x\n", result); 989 + return -EIO; 990 + } 991 + 992 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_apu_sppt"); 993 + 994 + return count; 995 + } 996 + static DEVICE_ATTR_WO(ppt_apu_sppt); 997 + 998 + /* Tunable: PPT platform SPPT ************************************************/ 999 + static ssize_t ppt_platform_sppt_store(struct device *dev, 1000 + struct device_attribute *attr, 1001 + const char *buf, size_t count) 1002 + { 1003 + int result, err; 1004 + u32 value; 1005 + 1006 + struct asus_wmi *asus = dev_get_drvdata(dev); 1007 + 1008 + result = kstrtou32(buf, 10, &value); 1009 + if (result) 1010 + return result; 1011 + 1012 + if (value < PPT_CPU_MIN || value > PPT_CPU_MAX) 1013 + return -EINVAL; 1014 + 1015 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PPT_PLAT_SPPT, value, &result); 1016 + if (err) { 1017 + pr_warn("Failed to set ppt_platform_sppt: %d\n", err); 1018 + return err; 1019 + } 1020 + 1021 + if (result > 1) { 1022 + pr_warn("Failed to set ppt_platform_sppt (result): 0x%x\n", result); 1023 + return -EIO; 1024 + } 1025 + 1026 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_platform_sppt"); 1027 + 1028 + return count; 1029 + } 1030 + static DEVICE_ATTR_WO(ppt_platform_sppt); 1031 + 1032 + /* Tunable: NVIDIA dynamic boost *********************************************/ 1033 + static ssize_t nv_dynamic_boost_store(struct device *dev, 1034 + struct device_attribute *attr, 1035 + const char *buf, size_t count) 1036 + { 1037 + int result, err; 1038 + u32 value; 1039 + 1040 + struct asus_wmi *asus = dev_get_drvdata(dev); 1041 + 1042 + result = kstrtou32(buf, 10, &value); 1043 + if (result) 1044 + return result; 1045 + 1046 + if (value < NVIDIA_BOOST_MIN || value > NVIDIA_BOOST_MAX) 1047 + return -EINVAL; 1048 + 1049 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_NV_DYN_BOOST, value, &result); 1050 + if (err) { 1051 + pr_warn("Failed to set nv_dynamic_boost: %d\n", err); 1052 + return err; 1053 + } 1054 + 1055 + if (result > 1) { 1056 + pr_warn("Failed to set nv_dynamic_boost (result): 0x%x\n", result); 1057 + return -EIO; 1058 + } 1059 + 1060 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_dynamic_boost"); 1061 + 1062 + return count; 1063 + } 1064 + static DEVICE_ATTR_WO(nv_dynamic_boost); 1065 + 1066 + /* Tunable: NVIDIA temperature target ****************************************/ 1067 + static ssize_t nv_temp_target_store(struct device *dev, 1068 + struct device_attribute *attr, 1069 + const char *buf, size_t count) 1070 + { 1071 + int result, err; 1072 + u32 value; 1073 + 1074 + struct asus_wmi *asus = dev_get_drvdata(dev); 1075 + 1076 + result = kstrtou32(buf, 10, &value); 1077 + if (result) 1078 + return result; 1079 + 1080 + if (value < NVIDIA_TEMP_MIN || value > NVIDIA_TEMP_MAX) 1081 + return -EINVAL; 1082 + 1083 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_NV_THERM_TARGET, value, &result); 1084 + if (err) { 1085 + pr_warn("Failed to set nv_temp_target: %d\n", err); 1086 + return err; 1087 + } 1088 + 1089 + if (result > 1) { 1090 + pr_warn("Failed to set nv_temp_target (result): 0x%x\n", result); 1091 + return -EIO; 1092 + } 1093 + 1094 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_temp_target"); 1095 + 1096 + return count; 1097 + } 1098 + static DEVICE_ATTR_WO(nv_temp_target); 976 1099 977 1100 /* Battery ********************************************************************/ 978 1101 ··· 2087 1734 } 2088 1735 static DEVICE_ATTR_RW(panel_od); 2089 1736 1737 + /* Mini-LED mode **************************************************************/ 1738 + static ssize_t mini_led_mode_show(struct device *dev, 1739 + struct device_attribute *attr, char *buf) 1740 + { 1741 + struct asus_wmi *asus = dev_get_drvdata(dev); 1742 + int result; 1743 + 1744 + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MINI_LED_MODE); 1745 + if (result < 0) 1746 + return result; 1747 + 1748 + return sysfs_emit(buf, "%d\n", result); 1749 + } 1750 + 1751 + static ssize_t mini_led_mode_store(struct device *dev, 1752 + struct device_attribute *attr, 1753 + const char *buf, size_t count) 1754 + { 1755 + int result, err; 1756 + u32 mode; 1757 + 1758 + struct asus_wmi *asus = dev_get_drvdata(dev); 1759 + 1760 + result = kstrtou32(buf, 10, &mode); 1761 + if (result) 1762 + return result; 1763 + 1764 + if (mode > 1) 1765 + return -EINVAL; 1766 + 1767 + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MINI_LED_MODE, mode, &result); 1768 + 1769 + if (err) { 1770 + pr_warn("Failed to set mini-LED: %d\n", err); 1771 + return err; 1772 + } 1773 + 1774 + if (result > 1) { 1775 + pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result); 1776 + return -EIO; 1777 + } 1778 + 1779 + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "mini_led_mode"); 1780 + 1781 + return count; 1782 + } 1783 + static DEVICE_ATTR_RW(mini_led_mode); 1784 + 2090 1785 /* Quirks *********************************************************************/ 2091 1786 2092 1787 static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) ··· 2471 2070 asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; 2472 2071 if (asus->gpu_fan_curve_available) 2473 2072 asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; 2073 + if (asus->mid_fan_curve_available) 2074 + asus->custom_fan_curves[FAN_CURVE_DEV_MID].enabled = false; 2474 2075 2475 2076 return count; 2476 2077 } ··· 2525 2122 return sysfs_emit(buf, "%s\n", ASUS_GPU_FAN_DESC); 2526 2123 } 2527 2124 2125 + /* Middle/Center fan on modern ROG laptops */ 2126 + static ssize_t fan3_input_show(struct device *dev, 2127 + struct device_attribute *attr, 2128 + char *buf) 2129 + { 2130 + struct asus_wmi *asus = dev_get_drvdata(dev); 2131 + int value; 2132 + int ret; 2133 + 2134 + ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_MID_FAN_CTRL, &value); 2135 + if (ret < 0) 2136 + return ret; 2137 + 2138 + value &= 0xffff; 2139 + 2140 + return sysfs_emit(buf, "%d\n", value * 100); 2141 + } 2142 + 2143 + static ssize_t fan3_label_show(struct device *dev, 2144 + struct device_attribute *attr, 2145 + char *buf) 2146 + { 2147 + return sysfs_emit(buf, "%s\n", ASUS_MID_FAN_DESC); 2148 + } 2149 + 2528 2150 static ssize_t pwm2_enable_show(struct device *dev, 2529 2151 struct device_attribute *attr, 2530 2152 char *buf) ··· 2596 2168 return count; 2597 2169 } 2598 2170 2171 + static ssize_t pwm3_enable_show(struct device *dev, 2172 + struct device_attribute *attr, 2173 + char *buf) 2174 + { 2175 + struct asus_wmi *asus = dev_get_drvdata(dev); 2176 + 2177 + return sysfs_emit(buf, "%d\n", asus->mid_fan_pwm_mode); 2178 + } 2179 + 2180 + static ssize_t pwm3_enable_store(struct device *dev, 2181 + struct device_attribute *attr, 2182 + const char *buf, size_t count) 2183 + { 2184 + struct asus_wmi *asus = dev_get_drvdata(dev); 2185 + int state; 2186 + int value; 2187 + int ret; 2188 + u32 retval; 2189 + 2190 + ret = kstrtouint(buf, 10, &state); 2191 + if (ret) 2192 + return ret; 2193 + 2194 + switch (state) { /* standard documented hwmon values */ 2195 + case ASUS_FAN_CTRL_FULLSPEED: 2196 + value = 1; 2197 + break; 2198 + case ASUS_FAN_CTRL_AUTO: 2199 + value = 0; 2200 + break; 2201 + default: 2202 + return -EINVAL; 2203 + } 2204 + 2205 + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_MID_FAN_CTRL, 2206 + value, &retval); 2207 + if (ret) 2208 + return ret; 2209 + 2210 + if (retval != 1) 2211 + return -EIO; 2212 + 2213 + asus->mid_fan_pwm_mode = state; 2214 + return count; 2215 + } 2216 + 2599 2217 /* Fan1 */ 2600 2218 static DEVICE_ATTR_RW(pwm1); 2601 2219 static DEVICE_ATTR_RW(pwm1_enable); ··· 2651 2177 static DEVICE_ATTR_RW(pwm2_enable); 2652 2178 static DEVICE_ATTR_RO(fan2_input); 2653 2179 static DEVICE_ATTR_RO(fan2_label); 2180 + /* Fan3 - Middle/center fan */ 2181 + static DEVICE_ATTR_RW(pwm3_enable); 2182 + static DEVICE_ATTR_RO(fan3_input); 2183 + static DEVICE_ATTR_RO(fan3_label); 2654 2184 2655 2185 /* Temperature */ 2656 2186 static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL); ··· 2663 2185 &dev_attr_pwm1.attr, 2664 2186 &dev_attr_pwm1_enable.attr, 2665 2187 &dev_attr_pwm2_enable.attr, 2188 + &dev_attr_pwm3_enable.attr, 2666 2189 &dev_attr_fan1_input.attr, 2667 2190 &dev_attr_fan1_label.attr, 2668 2191 &dev_attr_fan2_input.attr, 2669 2192 &dev_attr_fan2_label.attr, 2193 + &dev_attr_fan3_input.attr, 2194 + &dev_attr_fan3_label.attr, 2670 2195 2671 2196 &dev_attr_temp1_input.attr, 2672 2197 NULL ··· 2694 2213 || attr == &dev_attr_fan2_label.attr 2695 2214 || attr == &dev_attr_pwm2_enable.attr) { 2696 2215 if (asus->gpu_fan_type == FAN_TYPE_NONE) 2216 + return 0; 2217 + } else if (attr == &dev_attr_fan3_input.attr 2218 + || attr == &dev_attr_fan3_label.attr 2219 + || attr == &dev_attr_pwm3_enable.attr) { 2220 + if (asus->mid_fan_type == FAN_TYPE_NONE) 2697 2221 return 0; 2698 2222 } else if (attr == &dev_attr_temp1_input.attr) { 2699 2223 int err = asus_wmi_get_devstate(asus, ··· 2743 2257 static int asus_wmi_fan_init(struct asus_wmi *asus) 2744 2258 { 2745 2259 asus->gpu_fan_type = FAN_TYPE_NONE; 2260 + asus->mid_fan_type = FAN_TYPE_NONE; 2746 2261 asus->fan_type = FAN_TYPE_NONE; 2747 2262 asus->agfn_pwm = -1; 2748 2263 ··· 2757 2270 /* Modern models like G713 also have GPU fan control */ 2758 2271 if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_FAN_CTRL)) 2759 2272 asus->gpu_fan_type = FAN_TYPE_SPEC83; 2273 + 2274 + /* Some models also have a center/middle fan */ 2275 + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MID_FAN_CTRL)) 2276 + asus->mid_fan_type = FAN_TYPE_SPEC83; 2760 2277 2761 2278 if (asus->fan_type == FAN_TYPE_NONE) 2762 2279 return -ENODEV; ··· 2909 2418 { 2910 2419 struct fan_curve_data *curves; 2911 2420 u8 buf[FAN_CURVE_BUF_LEN]; 2912 - int fan_idx = 0; 2421 + int err, fan_idx; 2913 2422 u8 mode = 0; 2914 - int err; 2915 2423 2916 2424 if (asus->throttle_thermal_policy_available) 2917 2425 mode = asus->throttle_thermal_policy_mode; ··· 2920 2430 else if (mode == 1) 2921 2431 mode = 2; 2922 2432 2923 - if (fan_dev == ASUS_WMI_DEVID_GPU_FAN_CURVE) 2924 - fan_idx = FAN_CURVE_DEV_GPU; 2925 - 2926 - curves = &asus->custom_fan_curves[fan_idx]; 2927 2433 err = asus_wmi_evaluate_method_buf(asus->dsts_id, fan_dev, mode, buf, 2928 2434 FAN_CURVE_BUF_LEN); 2929 2435 if (err) { ··· 2927 2441 return err; 2928 2442 } 2929 2443 2930 - fan_curve_copy_from_buf(curves, buf); 2444 + fan_idx = FAN_CURVE_DEV_CPU; 2445 + if (fan_dev == ASUS_WMI_DEVID_GPU_FAN_CURVE) 2446 + fan_idx = FAN_CURVE_DEV_GPU; 2447 + 2448 + if (fan_dev == ASUS_WMI_DEVID_MID_FAN_CURVE) 2449 + fan_idx = FAN_CURVE_DEV_MID; 2450 + 2451 + curves = &asus->custom_fan_curves[fan_idx]; 2931 2452 curves->device_id = fan_dev; 2932 2453 2454 + fan_curve_copy_from_buf(curves, buf); 2933 2455 return 0; 2934 2456 } 2935 2457 ··· 2967 2473 { 2968 2474 int index = to_sensor_dev_attr(attr)->index; 2969 2475 2970 - return &asus->custom_fan_curves[index & FAN_CURVE_DEV_GPU]; 2476 + return &asus->custom_fan_curves[index]; 2971 2477 } 2972 2478 2973 2479 /* Determine which fan the attribute is for if SENSOR_ATTR_2 */ ··· 2976 2482 { 2977 2483 int nr = to_sensor_dev_attr_2(attr)->nr; 2978 2484 2979 - return &asus->custom_fan_curves[nr & FAN_CURVE_DEV_GPU]; 2485 + return &asus->custom_fan_curves[nr & ~FAN_CURVE_PWM_MASK]; 2980 2486 } 2981 2487 2982 2488 static ssize_t fan_curve_show(struct device *dev, ··· 2985 2491 struct sensor_device_attribute_2 *dev_attr = to_sensor_dev_attr_2(attr); 2986 2492 struct asus_wmi *asus = dev_get_drvdata(dev); 2987 2493 struct fan_curve_data *data; 2988 - int value, index, nr; 2494 + int value, pwm, index; 2989 2495 2990 2496 data = fan_curve_attr_2_select(asus, attr); 2497 + pwm = dev_attr->nr & FAN_CURVE_PWM_MASK; 2991 2498 index = dev_attr->index; 2992 - nr = dev_attr->nr; 2993 2499 2994 - if (nr & FAN_CURVE_PWM_MASK) 2500 + if (pwm) 2995 2501 value = data->percents[index]; 2996 2502 else 2997 2503 value = data->temps[index]; ··· 3034 2540 struct sensor_device_attribute_2 *dev_attr = to_sensor_dev_attr_2(attr); 3035 2541 struct asus_wmi *asus = dev_get_drvdata(dev); 3036 2542 struct fan_curve_data *data; 2543 + int err, pwm, index; 3037 2544 u8 value; 3038 - int err; 3039 - 3040 - int pwm = dev_attr->nr & FAN_CURVE_PWM_MASK; 3041 - int index = dev_attr->index; 3042 2545 3043 2546 data = fan_curve_attr_2_select(asus, attr); 2547 + pwm = dev_attr->nr & FAN_CURVE_PWM_MASK; 2548 + index = dev_attr->index; 3044 2549 3045 2550 err = kstrtou8(buf, 10, &value); 3046 2551 if (err < 0) 3047 2552 return err; 3048 2553 3049 - if (pwm) { 2554 + if (pwm) 3050 2555 data->percents[index] = value; 3051 - } else { 2556 + else 3052 2557 data->temps[index] = value; 3053 - } 3054 2558 3055 2559 /* 3056 2560 * Mark as disabled so the user has to explicitly enable to apply a ··· 3161 2669 FAN_CURVE_DEV_CPU, 7); 3162 2670 3163 2671 static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_pwm, fan_curve, 3164 - FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 0); 2672 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 0); 3165 2673 static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_pwm, fan_curve, 3166 2674 FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 1); 3167 2675 static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_pwm, fan_curve, ··· 3213 2721 static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_pwm, fan_curve, 3214 2722 FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 7); 3215 2723 2724 + /* MID */ 2725 + static SENSOR_DEVICE_ATTR_RW(pwm3_enable, fan_curve_enable, FAN_CURVE_DEV_MID); 2726 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point1_temp, fan_curve, 2727 + FAN_CURVE_DEV_MID, 0); 2728 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point2_temp, fan_curve, 2729 + FAN_CURVE_DEV_MID, 1); 2730 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point3_temp, fan_curve, 2731 + FAN_CURVE_DEV_MID, 2); 2732 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point4_temp, fan_curve, 2733 + FAN_CURVE_DEV_MID, 3); 2734 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point5_temp, fan_curve, 2735 + FAN_CURVE_DEV_MID, 4); 2736 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point6_temp, fan_curve, 2737 + FAN_CURVE_DEV_MID, 5); 2738 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point7_temp, fan_curve, 2739 + FAN_CURVE_DEV_MID, 6); 2740 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point8_temp, fan_curve, 2741 + FAN_CURVE_DEV_MID, 7); 2742 + 2743 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point1_pwm, fan_curve, 2744 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 0); 2745 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point2_pwm, fan_curve, 2746 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 1); 2747 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point3_pwm, fan_curve, 2748 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 2); 2749 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point4_pwm, fan_curve, 2750 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 3); 2751 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point5_pwm, fan_curve, 2752 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 4); 2753 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point6_pwm, fan_curve, 2754 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 5); 2755 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point7_pwm, fan_curve, 2756 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 6); 2757 + static SENSOR_DEVICE_ATTR_2_RW(pwm3_auto_point8_pwm, fan_curve, 2758 + FAN_CURVE_DEV_MID | FAN_CURVE_PWM_MASK, 7); 2759 + 3216 2760 static struct attribute *asus_fan_curve_attr[] = { 3217 2761 /* CPU */ 3218 2762 &sensor_dev_attr_pwm1_enable.dev_attr.attr, ··· 3286 2758 &sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr, 3287 2759 &sensor_dev_attr_pwm2_auto_point7_pwm.dev_attr.attr, 3288 2760 &sensor_dev_attr_pwm2_auto_point8_pwm.dev_attr.attr, 2761 + /* MID */ 2762 + &sensor_dev_attr_pwm3_enable.dev_attr.attr, 2763 + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, 2764 + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, 2765 + &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, 2766 + &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr, 2767 + &sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr, 2768 + &sensor_dev_attr_pwm3_auto_point6_temp.dev_attr.attr, 2769 + &sensor_dev_attr_pwm3_auto_point7_temp.dev_attr.attr, 2770 + &sensor_dev_attr_pwm3_auto_point8_temp.dev_attr.attr, 2771 + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, 2772 + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, 2773 + &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr, 2774 + &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr, 2775 + &sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr, 2776 + &sensor_dev_attr_pwm3_auto_point6_pwm.dev_attr.attr, 2777 + &sensor_dev_attr_pwm3_auto_point7_pwm.dev_attr.attr, 2778 + &sensor_dev_attr_pwm3_auto_point8_pwm.dev_attr.attr, 3289 2779 NULL 3290 2780 }; 3291 2781 ··· 3321 2775 return 0644; 3322 2776 3323 2777 if (asus->gpu_fan_curve_available && attr->name[3] == '2') 2778 + return 0644; 2779 + 2780 + if (asus->mid_fan_curve_available && attr->name[3] == '3') 3324 2781 return 0644; 3325 2782 3326 2783 return 0; ··· 3355 2806 if (err) 3356 2807 return err; 3357 2808 3358 - if (!asus->cpu_fan_curve_available && !asus->gpu_fan_curve_available) 2809 + err = fan_curve_check_present(asus, &asus->mid_fan_curve_available, 2810 + ASUS_WMI_DEVID_MID_FAN_CURVE); 2811 + if (err) 2812 + return err; 2813 + 2814 + if (!asus->cpu_fan_curve_available 2815 + && !asus->gpu_fan_curve_available 2816 + && !asus->mid_fan_curve_available) 3359 2817 return 0; 3360 2818 3361 2819 hwmon = devm_hwmon_device_register_with_groups( ··· 3431 2875 asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; 3432 2876 if (asus->gpu_fan_curve_available) 3433 2877 asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; 2878 + if (asus->mid_fan_curve_available) 2879 + asus->custom_fan_curves[FAN_CURVE_DEV_MID].enabled = false; 3434 2880 3435 2881 return 0; 3436 2882 } ··· 4030 3472 &dev_attr_camera.attr, 4031 3473 &dev_attr_cardr.attr, 4032 3474 &dev_attr_touchpad.attr, 3475 + &dev_attr_charge_mode.attr, 4033 3476 &dev_attr_egpu_enable.attr, 3477 + &dev_attr_egpu_connected.attr, 4034 3478 &dev_attr_dgpu_disable.attr, 4035 3479 &dev_attr_gpu_mux_mode.attr, 4036 3480 &dev_attr_lid_resume.attr, 4037 3481 &dev_attr_als_enable.attr, 4038 3482 &dev_attr_fan_boost_mode.attr, 4039 3483 &dev_attr_throttle_thermal_policy.attr, 3484 + &dev_attr_ppt_pl2_sppt.attr, 3485 + &dev_attr_ppt_pl1_spl.attr, 3486 + &dev_attr_ppt_fppt.attr, 3487 + &dev_attr_ppt_apu_sppt.attr, 3488 + &dev_attr_ppt_platform_sppt.attr, 3489 + &dev_attr_nv_dynamic_boost.attr, 3490 + &dev_attr_nv_temp_target.attr, 4040 3491 &dev_attr_panel_od.attr, 3492 + &dev_attr_mini_led_mode.attr, 4041 3493 NULL 4042 3494 }; 4043 3495 ··· 4069 3501 devid = ASUS_WMI_DEVID_LID_RESUME; 4070 3502 else if (attr == &dev_attr_als_enable.attr) 4071 3503 devid = ASUS_WMI_DEVID_ALS_ENABLE; 3504 + else if (attr == &dev_attr_charge_mode.attr) 3505 + ok = asus->charge_mode_available; 4072 3506 else if (attr == &dev_attr_egpu_enable.attr) 4073 3507 ok = asus->egpu_enable_available; 3508 + else if (attr == &dev_attr_egpu_connected.attr) 3509 + ok = asus->egpu_connect_available; 4074 3510 else if (attr == &dev_attr_dgpu_disable.attr) 4075 3511 ok = asus->dgpu_disable_available; 4076 3512 else if (attr == &dev_attr_gpu_mux_mode.attr) ··· 4083 3511 ok = asus->fan_boost_mode_available; 4084 3512 else if (attr == &dev_attr_throttle_thermal_policy.attr) 4085 3513 ok = asus->throttle_thermal_policy_available; 3514 + else if (attr == &dev_attr_ppt_pl2_sppt.attr) 3515 + ok = asus->ppt_pl2_sppt_available; 3516 + else if (attr == &dev_attr_ppt_pl1_spl.attr) 3517 + ok = asus->ppt_pl1_spl_available; 3518 + else if (attr == &dev_attr_ppt_fppt.attr) 3519 + ok = asus->ppt_fppt_available; 3520 + else if (attr == &dev_attr_ppt_apu_sppt.attr) 3521 + ok = asus->ppt_apu_sppt_available; 3522 + else if (attr == &dev_attr_ppt_platform_sppt.attr) 3523 + ok = asus->ppt_plat_sppt_available; 3524 + else if (attr == &dev_attr_nv_dynamic_boost.attr) 3525 + ok = asus->nv_dyn_boost_available; 3526 + else if (attr == &dev_attr_nv_temp_target.attr) 3527 + ok = asus->nv_temp_tgt_available; 4086 3528 else if (attr == &dev_attr_panel_od.attr) 4087 3529 ok = asus->panel_overdrive_available; 3530 + else if (attr == &dev_attr_mini_led_mode.attr) 3531 + ok = asus->mini_led_mode_available; 4088 3532 4089 3533 if (devid != -1) 4090 3534 ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); ··· 4355 3767 if (err) 4356 3768 goto fail_platform; 4357 3769 3770 + asus->charge_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CHARGE_MODE); 4358 3771 asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU); 3772 + asus->egpu_connect_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU_CONNECTED); 4359 3773 asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU); 4360 3774 asus->gpu_mux_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX); 4361 3775 asus->kbd_rgb_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE); 4362 3776 asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); 3777 + asus->ppt_pl2_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL2_SPPT); 3778 + asus->ppt_pl1_spl_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL1_SPL); 3779 + asus->ppt_fppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_FPPT); 3780 + asus->ppt_apu_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_APU_SPPT); 3781 + asus->ppt_plat_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PLAT_SPPT); 3782 + asus->nv_dyn_boost_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_DYN_BOOST); 3783 + asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET); 4363 3784 asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD); 3785 + asus->mini_led_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE); 4364 3786 4365 3787 err = fan_boost_mode_check_present(asus); 4366 3788 if (err)
+6 -3
drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
··· 393 393 struct kobject *attr_name_kobj; //individual attribute names 394 394 union acpi_object *obj = NULL; 395 395 union acpi_object *elements; 396 + struct kobject *duplicate; 396 397 struct kset *tmp_set; 397 398 int min_elements; 398 399 ··· 452 451 else 453 452 tmp_set = wmi_priv.main_dir_kset; 454 453 455 - if (kset_find_obj(tmp_set, elements[ATTR_NAME].string.pointer)) { 456 - pr_debug("duplicate attribute name found - %s\n", 457 - elements[ATTR_NAME].string.pointer); 454 + duplicate = kset_find_obj(tmp_set, elements[ATTR_NAME].string.pointer); 455 + if (duplicate) { 456 + pr_debug("Duplicate attribute name found - %s\n", 457 + elements[ATTR_NAME].string.pointer); 458 + kobject_put(duplicate); 458 459 goto nextobj; 459 460 } 460 461
+16
drivers/platform/x86/hp/Kconfig
··· 60 60 This is a driver for the WMI extensions (wireless and bluetooth power 61 61 control) of the HP Compaq TC1100 tablet. 62 62 63 + config HP_BIOSCFG 64 + tristate "HP BIOS Configuration Driver" 65 + default m 66 + depends on ACPI_WMI 67 + select NLS 68 + select FW_ATTR_CLASS 69 + help 70 + This driver enables administrators to securely manage BIOS settings 71 + using digital certificates and public-key cryptography that eliminate 72 + the need for passwords for both remote and local management. It supports 73 + changing BIOS settings on many HP machines from 2018 and newer without 74 + the use of any additional software. 75 + 76 + To compile this driver as a module, choose M here: the module will 77 + be called hp-bioscfg. 78 + 63 79 endif # X86_PLATFORM_DRIVERS_HP
+1
drivers/platform/x86/hp/Makefile
··· 8 8 obj-$(CONFIG_HP_ACCEL) += hp_accel.o 9 9 obj-$(CONFIG_HP_WMI) += hp-wmi.o 10 10 obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 11 + obj-$(CONFIG_HP_BIOSCFG) += hp-bioscfg/
+11
drivers/platform/x86/hp/hp-bioscfg/Makefile
··· 1 + obj-$(CONFIG_HP_BIOSCFG) := hp-bioscfg.o 2 + 3 + hp-bioscfg-objs := bioscfg.o \ 4 + biosattr-interface.o \ 5 + enum-attributes.o \ 6 + int-attributes.o \ 7 + order-list-attributes.o \ 8 + passwdobj-attributes.o \ 9 + spmobj-attributes.o \ 10 + string-attributes.o \ 11 + surestart-attributes.o
+312
drivers/platform/x86/hp/hp-bioscfg/biosattr-interface.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to methods under BIOS interface GUID 4 + * for use with hp-bioscfg driver. 5 + * 6 + * Copyright (c) 2022 Hewlett-Packard Inc. 7 + */ 8 + 9 + #include <linux/wmi.h> 10 + #include "bioscfg.h" 11 + 12 + /* 13 + * struct bios_args buffer is dynamically allocated. New WMI command types 14 + * were introduced that exceeds 128-byte data size. Changes to handle 15 + * the data size allocation scheme were kept in hp_wmi_perform_query function. 16 + */ 17 + struct bios_args { 18 + u32 signature; 19 + u32 command; 20 + u32 commandtype; 21 + u32 datasize; 22 + u8 data[]; 23 + }; 24 + 25 + /** 26 + * hp_set_attribute 27 + * 28 + * @a_name: The attribute name 29 + * @a_value: The attribute value 30 + * 31 + * Sets an attribute to new value 32 + * 33 + * Returns zero on success 34 + * -ENODEV if device is not found 35 + * -EINVAL if the instance of 'Setup Admin' password is not found. 36 + * -ENOMEM unable to allocate memory 37 + */ 38 + int hp_set_attribute(const char *a_name, const char *a_value) 39 + { 40 + int security_area_size; 41 + int a_name_size, a_value_size; 42 + u16 *buffer = NULL; 43 + u16 *start; 44 + int buffer_size, instance, ret; 45 + char *auth_token_choice; 46 + 47 + mutex_lock(&bioscfg_drv.mutex); 48 + 49 + instance = hp_get_password_instance_for_type(SETUP_PASSWD); 50 + if (instance < 0) { 51 + ret = -EINVAL; 52 + goto out_set_attribute; 53 + } 54 + 55 + /* Select which auth token to use; password or [auth token] */ 56 + if (bioscfg_drv.spm_data.auth_token) 57 + auth_token_choice = bioscfg_drv.spm_data.auth_token; 58 + else 59 + auth_token_choice = bioscfg_drv.password_data[instance].current_password; 60 + 61 + a_name_size = hp_calculate_string_buffer(a_name); 62 + a_value_size = hp_calculate_string_buffer(a_value); 63 + security_area_size = hp_calculate_security_buffer(auth_token_choice); 64 + buffer_size = a_name_size + a_value_size + security_area_size; 65 + 66 + buffer = kmalloc(buffer_size + 1, GFP_KERNEL); 67 + if (!buffer) { 68 + ret = -ENOMEM; 69 + goto out_set_attribute; 70 + } 71 + 72 + /* build variables to set */ 73 + start = buffer; 74 + start = hp_ascii_to_utf16_unicode(start, a_name); 75 + if (!start) { 76 + ret = -EINVAL; 77 + goto out_set_attribute; 78 + } 79 + 80 + start = hp_ascii_to_utf16_unicode(start, a_value); 81 + if (!start) { 82 + ret = -EINVAL; 83 + goto out_set_attribute; 84 + } 85 + 86 + ret = hp_populate_security_buffer(start, auth_token_choice); 87 + if (ret < 0) 88 + goto out_set_attribute; 89 + 90 + ret = hp_wmi_set_bios_setting(buffer, buffer_size); 91 + 92 + out_set_attribute: 93 + kfree(buffer); 94 + mutex_unlock(&bioscfg_drv.mutex); 95 + return ret; 96 + } 97 + 98 + /** 99 + * hp_wmi_perform_query 100 + * 101 + * @query: The commandtype (enum hp_wmi_commandtype) 102 + * @command: The command (enum hp_wmi_command) 103 + * @buffer: Buffer used as input and/or output 104 + * @insize: Size of input buffer 105 + * @outsize: Size of output buffer 106 + * 107 + * returns zero on success 108 + * an HP WMI query specific error code (which is positive) 109 + * -EINVAL if the query was not successful at all 110 + * -EINVAL if the output buffer size exceeds buffersize 111 + * 112 + * Note: The buffersize must at least be the maximum of the input and output 113 + * size. E.g. Battery info query is defined to have 1 byte input 114 + * and 128 byte output. The caller would do: 115 + * buffer = kzalloc(128, GFP_KERNEL); 116 + * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, 117 + * buffer, 1, 128) 118 + */ 119 + int hp_wmi_perform_query(int query, enum hp_wmi_command command, void *buffer, 120 + u32 insize, u32 outsize) 121 + { 122 + struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; 123 + struct bios_return *bios_return; 124 + union acpi_object *obj = NULL; 125 + struct bios_args *args = NULL; 126 + int mid, actual_outsize, ret; 127 + size_t bios_args_size; 128 + 129 + mid = hp_encode_outsize_for_pvsz(outsize); 130 + if (WARN_ON(mid < 0)) 131 + return mid; 132 + 133 + bios_args_size = struct_size(args, data, insize); 134 + args = kmalloc(bios_args_size, GFP_KERNEL); 135 + if (!args) 136 + return -ENOMEM; 137 + 138 + input.length = bios_args_size; 139 + input.pointer = args; 140 + 141 + /* BIOS expects 'SECU' in hex as the signature value*/ 142 + args->signature = 0x55434553; 143 + args->command = command; 144 + args->commandtype = query; 145 + args->datasize = insize; 146 + memcpy(args->data, buffer, flex_array_size(args, data, insize)); 147 + 148 + ret = wmi_evaluate_method(HP_WMI_BIOS_GUID, 0, mid, &input, &output); 149 + if (ret) 150 + goto out_free; 151 + 152 + obj = output.pointer; 153 + if (!obj) { 154 + ret = -EINVAL; 155 + goto out_free; 156 + } 157 + 158 + if (obj->type != ACPI_TYPE_BUFFER || 159 + obj->buffer.length < sizeof(*bios_return)) { 160 + pr_warn("query 0x%x returned wrong type or too small buffer\n", query); 161 + ret = -EINVAL; 162 + goto out_free; 163 + } 164 + 165 + bios_return = (struct bios_return *)obj->buffer.pointer; 166 + ret = bios_return->return_code; 167 + if (ret) { 168 + if (ret != INVALID_CMD_VALUE && ret != INVALID_CMD_TYPE) 169 + pr_warn("query 0x%x returned error 0x%x\n", query, ret); 170 + goto out_free; 171 + } 172 + 173 + /* Ignore output data of zero size */ 174 + if (!outsize) 175 + goto out_free; 176 + 177 + actual_outsize = min_t(u32, outsize, obj->buffer.length - sizeof(*bios_return)); 178 + memcpy_and_pad(buffer, outsize, obj->buffer.pointer + sizeof(*bios_return), 179 + actual_outsize, 0); 180 + 181 + out_free: 182 + ret = hp_wmi_error_and_message(ret); 183 + 184 + kfree(obj); 185 + kfree(args); 186 + return ret; 187 + } 188 + 189 + static void *utf16_empty_string(u16 *p) 190 + { 191 + *p++ = 2; 192 + *p++ = 0x00; 193 + return p; 194 + } 195 + 196 + /** 197 + * hp_ascii_to_utf16_unicode - Convert ascii string to UTF-16 unicode 198 + * 199 + * BIOS supports UTF-16 characters that are 2 bytes long. No variable 200 + * multi-byte language supported. 201 + * 202 + * @p: Unicode buffer address 203 + * @str: string to convert to unicode 204 + * 205 + * Returns a void pointer to the buffer string 206 + */ 207 + void *hp_ascii_to_utf16_unicode(u16 *p, const u8 *str) 208 + { 209 + int len = strlen(str); 210 + int ret; 211 + 212 + /* 213 + * Add null character when reading an empty string 214 + * "02 00 00 00" 215 + */ 216 + if (len == 0) 217 + return utf16_empty_string(p); 218 + 219 + /* Move pointer len * 2 number of bytes */ 220 + *p++ = len * 2; 221 + ret = utf8s_to_utf16s(str, strlen(str), UTF16_HOST_ENDIAN, p, len); 222 + if (ret < 0) { 223 + dev_err(bioscfg_drv.class_dev, "UTF16 conversion failed\n"); 224 + return NULL; 225 + } 226 + 227 + if (ret * sizeof(u16) > U16_MAX) { 228 + dev_err(bioscfg_drv.class_dev, "Error string too long\n"); 229 + return NULL; 230 + } 231 + 232 + p += len; 233 + return p; 234 + } 235 + 236 + /** 237 + * hp_wmi_set_bios_setting - Set setting's value in BIOS 238 + * 239 + * @input_buffer: Input buffer address 240 + * @input_size: Input buffer size 241 + * 242 + * Returns: Count of unicode characters written to BIOS if successful, otherwise 243 + * -ENOMEM unable to allocate memory 244 + * -EINVAL buffer not allocated or too small 245 + */ 246 + int hp_wmi_set_bios_setting(u16 *input_buffer, u32 input_size) 247 + { 248 + union acpi_object *obj; 249 + struct acpi_buffer input = {input_size, input_buffer}; 250 + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; 251 + int ret; 252 + 253 + ret = wmi_evaluate_method(HP_WMI_SET_BIOS_SETTING_GUID, 0, 1, &input, &output); 254 + 255 + obj = output.pointer; 256 + if (!obj) 257 + return -EINVAL; 258 + 259 + if (obj->type != ACPI_TYPE_INTEGER) { 260 + ret = -EINVAL; 261 + goto out_free; 262 + } 263 + 264 + ret = obj->integer.value; 265 + if (ret) { 266 + ret = hp_wmi_error_and_message(ret); 267 + goto out_free; 268 + } 269 + 270 + out_free: 271 + kfree(obj); 272 + return ret; 273 + } 274 + 275 + static int hp_attr_set_interface_probe(struct wmi_device *wdev, const void *context) 276 + { 277 + mutex_lock(&bioscfg_drv.mutex); 278 + mutex_unlock(&bioscfg_drv.mutex); 279 + return 0; 280 + } 281 + 282 + static void hp_attr_set_interface_remove(struct wmi_device *wdev) 283 + { 284 + mutex_lock(&bioscfg_drv.mutex); 285 + mutex_unlock(&bioscfg_drv.mutex); 286 + } 287 + 288 + static const struct wmi_device_id hp_attr_set_interface_id_table[] = { 289 + { .guid_string = HP_WMI_BIOS_GUID}, 290 + { } 291 + }; 292 + 293 + static struct wmi_driver hp_attr_set_interface_driver = { 294 + .driver = { 295 + .name = DRIVER_NAME, 296 + }, 297 + .probe = hp_attr_set_interface_probe, 298 + .remove = hp_attr_set_interface_remove, 299 + .id_table = hp_attr_set_interface_id_table, 300 + }; 301 + 302 + int hp_init_attr_set_interface(void) 303 + { 304 + return wmi_driver_register(&hp_attr_set_interface_driver); 305 + } 306 + 307 + void hp_exit_attr_set_interface(void) 308 + { 309 + wmi_driver_unregister(&hp_attr_set_interface_driver); 310 + } 311 + 312 + MODULE_DEVICE_TABLE(wmi, hp_attr_set_interface_id_table);
+1063
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Common methods for use with hp-bioscfg driver 4 + * 5 + * Copyright (c) 2022 HP Development Company, L.P. 6 + */ 7 + 8 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 + 10 + #include <linux/fs.h> 11 + #include <linux/module.h> 12 + #include <linux/kernel.h> 13 + #include <linux/wmi.h> 14 + #include "bioscfg.h" 15 + #include "../../firmware_attributes_class.h" 16 + #include <linux/nls.h> 17 + #include <linux/errno.h> 18 + 19 + MODULE_AUTHOR("Jorge Lopez <jorge.lopez2@hp.com>"); 20 + MODULE_DESCRIPTION("HP BIOS Configuration Driver"); 21 + MODULE_LICENSE("GPL"); 22 + 23 + struct bioscfg_priv bioscfg_drv = { 24 + .mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex), 25 + }; 26 + 27 + static struct class *fw_attr_class; 28 + 29 + ssize_t display_name_language_code_show(struct kobject *kobj, 30 + struct kobj_attribute *attr, 31 + char *buf) 32 + { 33 + return sysfs_emit(buf, "%s\n", LANG_CODE_STR); 34 + } 35 + 36 + struct kobj_attribute common_display_langcode = 37 + __ATTR_RO(display_name_language_code); 38 + 39 + int hp_get_integer_from_buffer(u8 **buffer, u32 *buffer_size, u32 *integer) 40 + { 41 + int *ptr = PTR_ALIGN((int *)*buffer, sizeof(int)); 42 + 43 + /* Ensure there is enough space remaining to read the integer */ 44 + if (*buffer_size < sizeof(int)) 45 + return -EINVAL; 46 + 47 + *integer = *(ptr++); 48 + *buffer = (u8 *)ptr; 49 + *buffer_size -= sizeof(int); 50 + 51 + return 0; 52 + } 53 + 54 + int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_size) 55 + { 56 + u16 *src = (u16 *)*buffer; 57 + u16 src_size; 58 + 59 + u16 size; 60 + int i; 61 + int conv_dst_size; 62 + 63 + if (*buffer_size < sizeof(u16)) 64 + return -EINVAL; 65 + 66 + src_size = *(src++); 67 + /* size value in u16 chars */ 68 + size = src_size / sizeof(u16); 69 + 70 + /* Ensure there is enough space remaining to read and convert 71 + * the string 72 + */ 73 + if (*buffer_size < src_size) 74 + return -EINVAL; 75 + 76 + for (i = 0; i < size; i++) 77 + if (src[i] == '\\' || 78 + src[i] == '\r' || 79 + src[i] == '\n' || 80 + src[i] == '\t') 81 + size++; 82 + 83 + /* 84 + * Conversion is limited to destination string max number of 85 + * bytes. 86 + */ 87 + conv_dst_size = size; 88 + if (size > dst_size) 89 + conv_dst_size = dst_size - 1; 90 + 91 + /* 92 + * convert from UTF-16 unicode to ASCII 93 + */ 94 + utf16s_to_utf8s(src, src_size, UTF16_HOST_ENDIAN, dst, conv_dst_size); 95 + dst[conv_dst_size] = 0; 96 + 97 + for (i = 0; i < conv_dst_size; i++) { 98 + if (*src == '\\' || 99 + *src == '\r' || 100 + *src == '\n' || 101 + *src == '\t') { 102 + dst[i++] = '\\'; 103 + if (i == conv_dst_size) 104 + break; 105 + } 106 + 107 + if (*src == '\r') 108 + dst[i] = 'r'; 109 + else if (*src == '\n') 110 + dst[i] = 'n'; 111 + else if (*src == '\t') 112 + dst[i] = 't'; 113 + else if (*src == '"') 114 + dst[i] = '\''; 115 + else 116 + dst[i] = *src; 117 + src++; 118 + } 119 + 120 + *buffer = (u8 *)src; 121 + *buffer_size -= size * sizeof(u16); 122 + 123 + return size; 124 + } 125 + 126 + int hp_get_common_data_from_buffer(u8 **buffer_ptr, u32 *buffer_size, 127 + struct common_data *common_data) 128 + { 129 + int ret = 0; 130 + int reqs; 131 + 132 + // PATH: 133 + ret = hp_get_string_from_buffer(buffer_ptr, buffer_size, common_data->path, 134 + sizeof(common_data->path)); 135 + if (ret < 0) 136 + goto common_exit; 137 + 138 + // IS_READONLY: 139 + ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size, 140 + &common_data->is_readonly); 141 + if (ret < 0) 142 + goto common_exit; 143 + 144 + //DISPLAY_IN_UI: 145 + ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size, 146 + &common_data->display_in_ui); 147 + if (ret < 0) 148 + goto common_exit; 149 + 150 + // REQUIRES_PHYSICAL_PRESENCE: 151 + ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size, 152 + &common_data->requires_physical_presence); 153 + if (ret < 0) 154 + goto common_exit; 155 + 156 + // SEQUENCE: 157 + ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size, 158 + &common_data->sequence); 159 + if (ret < 0) 160 + goto common_exit; 161 + 162 + // PREREQUISITES_SIZE: 163 + ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size, 164 + &common_data->prerequisites_size); 165 + if (ret < 0) 166 + goto common_exit; 167 + 168 + if (common_data->prerequisites_size > MAX_PREREQUISITES_SIZE) { 169 + /* Report a message and limit prerequisite size to maximum value */ 170 + pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n"); 171 + common_data->prerequisites_size = MAX_PREREQUISITES_SIZE; 172 + } 173 + 174 + // PREREQUISITES: 175 + for (reqs = 0; reqs < common_data->prerequisites_size; reqs++) { 176 + ret = hp_get_string_from_buffer(buffer_ptr, buffer_size, 177 + common_data->prerequisites[reqs], 178 + sizeof(common_data->prerequisites[reqs])); 179 + if (ret < 0) 180 + break; 181 + } 182 + 183 + // SECURITY_LEVEL: 184 + ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size, 185 + &common_data->security_level); 186 + 187 + common_exit: 188 + return ret; 189 + } 190 + 191 + int hp_enforce_single_line_input(char *buf, size_t count) 192 + { 193 + char *p; 194 + 195 + p = memchr(buf, '\n', count); 196 + 197 + if (p == buf + count - 1) 198 + *p = '\0'; /* strip trailing newline */ 199 + else if (p) 200 + return -EINVAL; /* enforce single line input */ 201 + 202 + return 0; 203 + } 204 + 205 + /* Set pending reboot value and generate KOBJ_NAME event */ 206 + void hp_set_reboot_and_signal_event(void) 207 + { 208 + bioscfg_drv.pending_reboot = true; 209 + kobject_uevent(&bioscfg_drv.class_dev->kobj, KOBJ_CHANGE); 210 + } 211 + 212 + /** 213 + * hp_calculate_string_buffer() - determines size of string buffer for 214 + * use with BIOS communication 215 + * 216 + * @str: the string to calculate based upon 217 + */ 218 + size_t hp_calculate_string_buffer(const char *str) 219 + { 220 + size_t length = strlen(str); 221 + 222 + /* BIOS expects 4 bytes when an empty string is found */ 223 + if (length == 0) 224 + return 4; 225 + 226 + /* u16 length field + one UTF16 char for each input char */ 227 + return sizeof(u16) + strlen(str) * sizeof(u16); 228 + } 229 + 230 + int hp_wmi_error_and_message(int error_code) 231 + { 232 + char *error_msg = NULL; 233 + int ret; 234 + 235 + switch (error_code) { 236 + case SUCCESS: 237 + error_msg = "Success"; 238 + ret = 0; 239 + break; 240 + case CMD_FAILED: 241 + error_msg = "Command failed"; 242 + ret = -EINVAL; 243 + break; 244 + case INVALID_SIGN: 245 + error_msg = "Invalid signature"; 246 + ret = -EINVAL; 247 + break; 248 + case INVALID_CMD_VALUE: 249 + error_msg = "Invalid command value/Feature not supported"; 250 + ret = -EOPNOTSUPP; 251 + break; 252 + case INVALID_CMD_TYPE: 253 + error_msg = "Invalid command type"; 254 + ret = -EINVAL; 255 + break; 256 + case INVALID_DATA_SIZE: 257 + error_msg = "Invalid data size"; 258 + ret = -EINVAL; 259 + break; 260 + case INVALID_CMD_PARAM: 261 + error_msg = "Invalid command parameter"; 262 + ret = -EINVAL; 263 + break; 264 + case ENCRYP_CMD_REQUIRED: 265 + error_msg = "Secure/encrypted command required"; 266 + ret = -EACCES; 267 + break; 268 + case NO_SECURE_SESSION: 269 + error_msg = "No secure session established"; 270 + ret = -EACCES; 271 + break; 272 + case SECURE_SESSION_FOUND: 273 + error_msg = "Secure session already established"; 274 + ret = -EACCES; 275 + break; 276 + case SECURE_SESSION_FAILED: 277 + error_msg = "Secure session failed"; 278 + ret = -EIO; 279 + break; 280 + case AUTH_FAILED: 281 + error_msg = "Other permission/Authentication failed"; 282 + ret = -EACCES; 283 + break; 284 + case INVALID_BIOS_AUTH: 285 + error_msg = "Invalid BIOS administrator password"; 286 + ret = -EINVAL; 287 + break; 288 + case NONCE_DID_NOT_MATCH: 289 + error_msg = "Nonce did not match"; 290 + ret = -EINVAL; 291 + break; 292 + case GENERIC_ERROR: 293 + error_msg = "Generic/Other error"; 294 + ret = -EIO; 295 + break; 296 + case BIOS_ADMIN_POLICY_NOT_MET: 297 + error_msg = "BIOS Admin password does not meet password policy requirements"; 298 + ret = -EINVAL; 299 + break; 300 + case BIOS_ADMIN_NOT_SET: 301 + error_msg = "BIOS Setup password is not set"; 302 + ret = -EPERM; 303 + break; 304 + case P21_NO_PROVISIONED: 305 + error_msg = "P21 is not provisioned"; 306 + ret = -EPERM; 307 + break; 308 + case P21_PROVISION_IN_PROGRESS: 309 + error_msg = "P21 is already provisioned or provisioning is in progress and a signing key has already been sent"; 310 + ret = -EINPROGRESS; 311 + break; 312 + case P21_IN_USE: 313 + error_msg = "P21 in use (cannot deprovision)"; 314 + ret = -EPERM; 315 + break; 316 + case HEP_NOT_ACTIVE: 317 + error_msg = "HEP not activated"; 318 + ret = -EPERM; 319 + break; 320 + case HEP_ALREADY_SET: 321 + error_msg = "HEP Transport already set"; 322 + ret = -EINVAL; 323 + break; 324 + case HEP_CHECK_STATE: 325 + error_msg = "Check the current HEP state"; 326 + ret = -EINVAL; 327 + break; 328 + default: 329 + error_msg = "Generic/Other error"; 330 + ret = -EIO; 331 + break; 332 + } 333 + 334 + if (error_code) 335 + pr_warn_ratelimited("Returned error 0x%x, \"%s\"\n", error_code, error_msg); 336 + 337 + return ret; 338 + } 339 + 340 + static ssize_t pending_reboot_show(struct kobject *kobj, 341 + struct kobj_attribute *attr, 342 + char *buf) 343 + { 344 + return sysfs_emit(buf, "%d\n", bioscfg_drv.pending_reboot); 345 + } 346 + 347 + static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot); 348 + 349 + /* 350 + * create_attributes_level_sysfs_files() - Creates pending_reboot attributes 351 + */ 352 + static int create_attributes_level_sysfs_files(void) 353 + { 354 + return sysfs_create_file(&bioscfg_drv.main_dir_kset->kobj, 355 + &pending_reboot.attr); 356 + } 357 + 358 + static void attr_name_release(struct kobject *kobj) 359 + { 360 + kfree(kobj); 361 + } 362 + 363 + static const struct kobj_type attr_name_ktype = { 364 + .release = attr_name_release, 365 + .sysfs_ops = &kobj_sysfs_ops, 366 + }; 367 + 368 + /** 369 + * hp_get_wmiobj_pointer() - Get Content of WMI block for particular instance 370 + * 371 + * @instance_id: WMI instance ID 372 + * @guid_string: WMI GUID (in str form) 373 + * 374 + * Fetches the content for WMI block (instance_id) under GUID (guid_string) 375 + * Caller must kfree the return 376 + */ 377 + union acpi_object *hp_get_wmiobj_pointer(int instance_id, const char *guid_string) 378 + { 379 + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 380 + acpi_status status; 381 + 382 + status = wmi_query_block(guid_string, instance_id, &out); 383 + return ACPI_SUCCESS(status) ? (union acpi_object *)out.pointer : NULL; 384 + } 385 + 386 + /** 387 + * hp_get_instance_count() - Compute total number of instances under guid_string 388 + * 389 + * @guid_string: WMI GUID (in string form) 390 + */ 391 + int hp_get_instance_count(const char *guid_string) 392 + { 393 + union acpi_object *wmi_obj = NULL; 394 + int i = 0; 395 + 396 + do { 397 + kfree(wmi_obj); 398 + wmi_obj = hp_get_wmiobj_pointer(i, guid_string); 399 + i++; 400 + } while (wmi_obj); 401 + 402 + return i - 1; 403 + } 404 + 405 + /** 406 + * hp_alloc_attributes_data() - Allocate attributes data for a particular type 407 + * 408 + * @attr_type: Attribute type to allocate 409 + */ 410 + static int hp_alloc_attributes_data(int attr_type) 411 + { 412 + switch (attr_type) { 413 + case HPWMI_STRING_TYPE: 414 + return hp_alloc_string_data(); 415 + 416 + case HPWMI_INTEGER_TYPE: 417 + return hp_alloc_integer_data(); 418 + 419 + case HPWMI_ENUMERATION_TYPE: 420 + return hp_alloc_enumeration_data(); 421 + 422 + case HPWMI_ORDERED_LIST_TYPE: 423 + return hp_alloc_ordered_list_data(); 424 + 425 + case HPWMI_PASSWORD_TYPE: 426 + return hp_alloc_password_data(); 427 + 428 + default: 429 + return 0; 430 + } 431 + } 432 + 433 + int hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int *len) 434 + { 435 + int ret = 0; 436 + int new_len = 0; 437 + char tmp[] = "0x00"; 438 + char *new_str = NULL; 439 + long ch; 440 + int i; 441 + 442 + if (input_len <= 0 || !input || !str || !len) 443 + return -EINVAL; 444 + 445 + *len = 0; 446 + *str = NULL; 447 + 448 + new_str = kmalloc(input_len, GFP_KERNEL); 449 + if (!new_str) 450 + return -ENOMEM; 451 + 452 + for (i = 0; i < input_len; i += 5) { 453 + strncpy(tmp, input + i, strlen(tmp)); 454 + if (kstrtol(tmp, 16, &ch) == 0) { 455 + // escape char 456 + if (ch == '\\' || 457 + ch == '\r' || 458 + ch == '\n' || ch == '\t') { 459 + if (ch == '\r') 460 + ch = 'r'; 461 + else if (ch == '\n') 462 + ch = 'n'; 463 + else if (ch == '\t') 464 + ch = 't'; 465 + new_str[new_len++] = '\\'; 466 + } 467 + new_str[new_len++] = ch; 468 + if (ch == '\0') 469 + break; 470 + } 471 + } 472 + 473 + if (new_len) { 474 + new_str[new_len] = '\0'; 475 + *str = krealloc(new_str, (new_len + 1) * sizeof(char), 476 + GFP_KERNEL); 477 + if (*str) 478 + *len = new_len; 479 + else 480 + ret = -ENOMEM; 481 + } else { 482 + ret = -EFAULT; 483 + } 484 + 485 + if (ret) 486 + kfree(new_str); 487 + return ret; 488 + } 489 + 490 + /* map output size to the corresponding WMI method id */ 491 + int hp_encode_outsize_for_pvsz(int outsize) 492 + { 493 + if (outsize > 4096) 494 + return -EINVAL; 495 + if (outsize > 1024) 496 + return 5; 497 + if (outsize > 128) 498 + return 4; 499 + if (outsize > 4) 500 + return 3; 501 + if (outsize > 0) 502 + return 2; 503 + return 1; 504 + } 505 + 506 + /* 507 + * Update friendly display name for several attributes associated to 508 + * 'Schedule Power-On' 509 + */ 510 + void hp_friendly_user_name_update(char *path, const char *attr_name, 511 + char *attr_display, int attr_size) 512 + { 513 + if (strstr(path, SCHEDULE_POWER_ON)) 514 + snprintf(attr_display, attr_size, "%s - %s", SCHEDULE_POWER_ON, attr_name); 515 + else 516 + strscpy(attr_display, attr_name, attr_size); 517 + } 518 + 519 + /** 520 + * hp_update_attribute_permissions() - Update attributes permissions when 521 + * isReadOnly value is 1 522 + * 523 + * @is_readonly: bool value to indicate if it a readonly attribute. 524 + * @current_val: kobj_attribute corresponding to attribute. 525 + * 526 + */ 527 + void hp_update_attribute_permissions(bool is_readonly, struct kobj_attribute *current_val) 528 + { 529 + current_val->attr.mode = is_readonly ? 0444 : 0644; 530 + } 531 + 532 + /** 533 + * destroy_attribute_objs() - Free a kset of kobjects 534 + * @kset: The kset to destroy 535 + * 536 + * Fress kobjects created for each attribute_name under attribute type kset 537 + */ 538 + static void destroy_attribute_objs(struct kset *kset) 539 + { 540 + struct kobject *pos, *next; 541 + 542 + list_for_each_entry_safe(pos, next, &kset->list, entry) 543 + kobject_put(pos); 544 + } 545 + 546 + /** 547 + * release_attributes_data() - Clean-up all sysfs directories and files created 548 + */ 549 + static void release_attributes_data(void) 550 + { 551 + mutex_lock(&bioscfg_drv.mutex); 552 + 553 + hp_exit_string_attributes(); 554 + hp_exit_integer_attributes(); 555 + hp_exit_enumeration_attributes(); 556 + hp_exit_ordered_list_attributes(); 557 + hp_exit_password_attributes(); 558 + hp_exit_sure_start_attributes(); 559 + hp_exit_secure_platform_attributes(); 560 + 561 + if (bioscfg_drv.authentication_dir_kset) { 562 + destroy_attribute_objs(bioscfg_drv.authentication_dir_kset); 563 + kset_unregister(bioscfg_drv.authentication_dir_kset); 564 + bioscfg_drv.authentication_dir_kset = NULL; 565 + } 566 + if (bioscfg_drv.main_dir_kset) { 567 + sysfs_remove_file(&bioscfg_drv.main_dir_kset->kobj, &pending_reboot.attr); 568 + destroy_attribute_objs(bioscfg_drv.main_dir_kset); 569 + kset_unregister(bioscfg_drv.main_dir_kset); 570 + bioscfg_drv.main_dir_kset = NULL; 571 + } 572 + mutex_unlock(&bioscfg_drv.mutex); 573 + } 574 + 575 + /** 576 + * hp_add_other_attributes() - Initialize HP custom attributes not 577 + * reported by BIOS and required to support Secure Platform and Sure 578 + * Start. 579 + * 580 + * @attr_type: Custom HP attribute not reported by BIOS 581 + * 582 + * Initialize all 2 types of attributes: Platform and Sure Start 583 + * object. Populates each attribute types respective properties 584 + * under sysfs files. 585 + * 586 + * Returns zero(0) if successful. Otherwise, a negative value. 587 + */ 588 + static int hp_add_other_attributes(int attr_type) 589 + { 590 + struct kobject *attr_name_kobj; 591 + union acpi_object *obj = NULL; 592 + int ret; 593 + char *attr_name; 594 + 595 + mutex_lock(&bioscfg_drv.mutex); 596 + 597 + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); 598 + if (!attr_name_kobj) { 599 + ret = -ENOMEM; 600 + goto err_other_attr_init; 601 + } 602 + 603 + /* Check if attribute type is supported */ 604 + switch (attr_type) { 605 + case HPWMI_SECURE_PLATFORM_TYPE: 606 + attr_name_kobj->kset = bioscfg_drv.authentication_dir_kset; 607 + attr_name = SPM_STR; 608 + break; 609 + 610 + case HPWMI_SURE_START_TYPE: 611 + attr_name_kobj->kset = bioscfg_drv.main_dir_kset; 612 + attr_name = SURE_START_STR; 613 + break; 614 + 615 + default: 616 + pr_err("Error: Unknown attr_type: %d\n", attr_type); 617 + ret = -EINVAL; 618 + goto err_other_attr_init; 619 + } 620 + 621 + ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, 622 + NULL, "%s", attr_name); 623 + if (ret) { 624 + pr_err("Error encountered [%d]\n", ret); 625 + kobject_put(attr_name_kobj); 626 + goto err_other_attr_init; 627 + } 628 + 629 + /* Populate attribute data */ 630 + switch (attr_type) { 631 + case HPWMI_SECURE_PLATFORM_TYPE: 632 + ret = hp_populate_secure_platform_data(attr_name_kobj); 633 + if (ret) 634 + goto err_other_attr_init; 635 + break; 636 + 637 + case HPWMI_SURE_START_TYPE: 638 + ret = hp_populate_sure_start_data(attr_name_kobj); 639 + if (ret) 640 + goto err_other_attr_init; 641 + break; 642 + 643 + default: 644 + ret = -EINVAL; 645 + goto err_other_attr_init; 646 + } 647 + 648 + mutex_unlock(&bioscfg_drv.mutex); 649 + return 0; 650 + 651 + err_other_attr_init: 652 + mutex_unlock(&bioscfg_drv.mutex); 653 + kfree(obj); 654 + return ret; 655 + } 656 + 657 + static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type, 658 + union acpi_object *obj, 659 + const char *guid, int min_elements, 660 + int instance_id) 661 + { 662 + struct kobject *attr_name_kobj; 663 + union acpi_object *elements; 664 + struct kset *temp_kset; 665 + 666 + char *str_value = NULL; 667 + int str_len; 668 + int ret = 0; 669 + 670 + /* Take action appropriate to each ACPI TYPE */ 671 + if (obj->package.count < min_elements) { 672 + pr_err("ACPI-package does not have enough elements: %d < %d\n", 673 + obj->package.count, min_elements); 674 + goto pack_attr_exit; 675 + } 676 + 677 + elements = obj->package.elements; 678 + 679 + /* sanity checking */ 680 + if (elements[NAME].type != ACPI_TYPE_STRING) { 681 + pr_debug("incorrect element type\n"); 682 + goto pack_attr_exit; 683 + } 684 + if (strlen(elements[NAME].string.pointer) == 0) { 685 + pr_debug("empty attribute found\n"); 686 + goto pack_attr_exit; 687 + } 688 + 689 + if (attr_type == HPWMI_PASSWORD_TYPE) 690 + temp_kset = bioscfg_drv.authentication_dir_kset; 691 + else 692 + temp_kset = bioscfg_drv.main_dir_kset; 693 + 694 + /* convert attribute name to string */ 695 + ret = hp_convert_hexstr_to_str(elements[NAME].string.pointer, 696 + elements[NAME].string.length, 697 + &str_value, &str_len); 698 + 699 + if (ret) { 700 + pr_debug("Failed to populate integer package data. Error [0%0x]\n", 701 + ret); 702 + kfree(str_value); 703 + return ret; 704 + } 705 + 706 + /* All duplicate attributes found are ignored */ 707 + if (kset_find_obj(temp_kset, str_value)) { 708 + pr_debug("Duplicate attribute name found - %s\n", str_value); 709 + goto pack_attr_exit; 710 + } 711 + 712 + /* build attribute */ 713 + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); 714 + if (!attr_name_kobj) { 715 + ret = -ENOMEM; 716 + goto pack_attr_exit; 717 + } 718 + 719 + attr_name_kobj->kset = temp_kset; 720 + 721 + ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, 722 + NULL, "%s", str_value); 723 + 724 + if (ret) { 725 + kobject_put(attr_name_kobj); 726 + goto pack_attr_exit; 727 + } 728 + 729 + /* enumerate all of these attributes */ 730 + switch (attr_type) { 731 + case HPWMI_STRING_TYPE: 732 + ret = hp_populate_string_package_data(elements, 733 + instance_id, 734 + attr_name_kobj); 735 + break; 736 + case HPWMI_INTEGER_TYPE: 737 + ret = hp_populate_integer_package_data(elements, 738 + instance_id, 739 + attr_name_kobj); 740 + break; 741 + case HPWMI_ENUMERATION_TYPE: 742 + ret = hp_populate_enumeration_package_data(elements, 743 + instance_id, 744 + attr_name_kobj); 745 + break; 746 + case HPWMI_ORDERED_LIST_TYPE: 747 + ret = hp_populate_ordered_list_package_data(elements, 748 + instance_id, 749 + attr_name_kobj); 750 + break; 751 + case HPWMI_PASSWORD_TYPE: 752 + ret = hp_populate_password_package_data(elements, 753 + instance_id, 754 + attr_name_kobj); 755 + break; 756 + default: 757 + pr_debug("Unknown attribute type found: 0x%x\n", attr_type); 758 + break; 759 + } 760 + 761 + pack_attr_exit: 762 + kfree(str_value); 763 + return ret; 764 + } 765 + 766 + static int hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type, 767 + union acpi_object *obj, 768 + const char *guid, int min_elements, 769 + int instance_id) 770 + { 771 + struct kobject *attr_name_kobj; 772 + struct kset *temp_kset; 773 + char str[MAX_BUFF_SIZE]; 774 + 775 + char *temp_str = NULL; 776 + char *str_value = NULL; 777 + u8 *buffer_ptr = NULL; 778 + int buffer_size; 779 + int ret = 0; 780 + 781 + buffer_size = obj->buffer.length; 782 + buffer_ptr = obj->buffer.pointer; 783 + 784 + ret = hp_get_string_from_buffer(&buffer_ptr, 785 + &buffer_size, str, MAX_BUFF_SIZE); 786 + 787 + if (ret < 0) 788 + goto buff_attr_exit; 789 + 790 + if (attr_type == HPWMI_PASSWORD_TYPE || 791 + attr_type == HPWMI_SECURE_PLATFORM_TYPE) 792 + temp_kset = bioscfg_drv.authentication_dir_kset; 793 + else 794 + temp_kset = bioscfg_drv.main_dir_kset; 795 + 796 + /* All duplicate attributes found are ignored */ 797 + if (kset_find_obj(temp_kset, str)) { 798 + pr_debug("Duplicate attribute name found - %s\n", str); 799 + goto buff_attr_exit; 800 + } 801 + 802 + /* build attribute */ 803 + attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); 804 + if (!attr_name_kobj) { 805 + ret = -ENOMEM; 806 + goto buff_attr_exit; 807 + } 808 + 809 + attr_name_kobj->kset = temp_kset; 810 + 811 + temp_str = str; 812 + if (attr_type == HPWMI_SECURE_PLATFORM_TYPE) 813 + temp_str = "SPM"; 814 + 815 + ret = kobject_init_and_add(attr_name_kobj, 816 + &attr_name_ktype, NULL, "%s", temp_str); 817 + if (ret) { 818 + kobject_put(attr_name_kobj); 819 + goto buff_attr_exit; 820 + } 821 + 822 + /* enumerate all of these attributes */ 823 + switch (attr_type) { 824 + case HPWMI_STRING_TYPE: 825 + ret = hp_populate_string_buffer_data(buffer_ptr, 826 + &buffer_size, 827 + instance_id, 828 + attr_name_kobj); 829 + break; 830 + case HPWMI_INTEGER_TYPE: 831 + ret = hp_populate_integer_buffer_data(buffer_ptr, 832 + &buffer_size, 833 + instance_id, 834 + attr_name_kobj); 835 + break; 836 + case HPWMI_ENUMERATION_TYPE: 837 + ret = hp_populate_enumeration_buffer_data(buffer_ptr, 838 + &buffer_size, 839 + instance_id, 840 + attr_name_kobj); 841 + break; 842 + case HPWMI_ORDERED_LIST_TYPE: 843 + ret = hp_populate_ordered_list_buffer_data(buffer_ptr, 844 + &buffer_size, 845 + instance_id, 846 + attr_name_kobj); 847 + break; 848 + case HPWMI_PASSWORD_TYPE: 849 + ret = hp_populate_password_buffer_data(buffer_ptr, 850 + &buffer_size, 851 + instance_id, 852 + attr_name_kobj); 853 + break; 854 + default: 855 + pr_debug("Unknown attribute type found: 0x%x\n", attr_type); 856 + break; 857 + } 858 + 859 + buff_attr_exit: 860 + kfree(str_value); 861 + return ret; 862 + } 863 + 864 + /** 865 + * hp_init_bios_attributes() - Initialize all attributes for a type 866 + * @attr_type: The attribute type to initialize 867 + * @guid: The WMI GUID associated with this type to initialize 868 + * 869 + * Initialize all 5 types of attributes: enumeration, integer, 870 + * string, password, ordered list object. Populates each attribute types 871 + * respective properties under sysfs files 872 + */ 873 + static int hp_init_bios_attributes(enum hp_wmi_data_type attr_type, const char *guid) 874 + { 875 + union acpi_object *obj = NULL; 876 + int min_elements; 877 + 878 + /* instance_id needs to be reset for each type GUID 879 + * also, instance IDs are unique within GUID but not across 880 + */ 881 + int instance_id = 0; 882 + int cur_instance_id = instance_id; 883 + int ret = 0; 884 + 885 + ret = hp_alloc_attributes_data(attr_type); 886 + if (ret) 887 + return ret; 888 + 889 + switch (attr_type) { 890 + case HPWMI_STRING_TYPE: 891 + min_elements = STR_ELEM_CNT; 892 + break; 893 + case HPWMI_INTEGER_TYPE: 894 + min_elements = INT_ELEM_CNT; 895 + break; 896 + case HPWMI_ENUMERATION_TYPE: 897 + min_elements = ENUM_ELEM_CNT; 898 + break; 899 + case HPWMI_ORDERED_LIST_TYPE: 900 + min_elements = ORD_ELEM_CNT; 901 + break; 902 + case HPWMI_PASSWORD_TYPE: 903 + min_elements = PSWD_ELEM_CNT; 904 + break; 905 + default: 906 + pr_err("Error: Unknown attr_type: %d\n", attr_type); 907 + return -EINVAL; 908 + } 909 + 910 + /* need to use specific instance_id and guid combination to get right data */ 911 + obj = hp_get_wmiobj_pointer(instance_id, guid); 912 + if (!obj) 913 + return -ENODEV; 914 + 915 + mutex_lock(&bioscfg_drv.mutex); 916 + while (obj) { 917 + /* Take action appropriate to each ACPI TYPE */ 918 + if (obj->type == ACPI_TYPE_PACKAGE) { 919 + ret = hp_init_bios_package_attribute(attr_type, obj, 920 + guid, min_elements, 921 + cur_instance_id); 922 + 923 + } else if (obj->type == ACPI_TYPE_BUFFER) { 924 + ret = hp_init_bios_buffer_attribute(attr_type, obj, 925 + guid, min_elements, 926 + cur_instance_id); 927 + 928 + } else { 929 + pr_err("Expected ACPI-package or buffer type, got: %d\n", 930 + obj->type); 931 + ret = -EIO; 932 + goto err_attr_init; 933 + } 934 + 935 + /* 936 + * Failure reported in one attribute must not 937 + * stop process of the remaining attribute values. 938 + */ 939 + if (ret >= 0) 940 + cur_instance_id++; 941 + 942 + kfree(obj); 943 + instance_id++; 944 + obj = hp_get_wmiobj_pointer(instance_id, guid); 945 + } 946 + 947 + err_attr_init: 948 + mutex_unlock(&bioscfg_drv.mutex); 949 + kfree(obj); 950 + return ret; 951 + } 952 + 953 + static int __init hp_init(void) 954 + { 955 + int ret; 956 + int hp_bios_capable = wmi_has_guid(HP_WMI_BIOS_GUID); 957 + int set_bios_settings = wmi_has_guid(HP_WMI_SET_BIOS_SETTING_GUID); 958 + 959 + if (!hp_bios_capable) { 960 + pr_err("Unable to run on non-HP system\n"); 961 + return -ENODEV; 962 + } 963 + 964 + if (!set_bios_settings) { 965 + pr_err("Unable to set BIOS settings on HP systems\n"); 966 + return -ENODEV; 967 + } 968 + 969 + ret = hp_init_attr_set_interface(); 970 + if (ret) 971 + return ret; 972 + 973 + ret = fw_attributes_class_get(&fw_attr_class); 974 + if (ret) 975 + goto err_unregister_class; 976 + 977 + bioscfg_drv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0), 978 + NULL, "%s", DRIVER_NAME); 979 + if (IS_ERR(bioscfg_drv.class_dev)) { 980 + ret = PTR_ERR(bioscfg_drv.class_dev); 981 + goto err_unregister_class; 982 + } 983 + 984 + bioscfg_drv.main_dir_kset = kset_create_and_add("attributes", NULL, 985 + &bioscfg_drv.class_dev->kobj); 986 + if (!bioscfg_drv.main_dir_kset) { 987 + ret = -ENOMEM; 988 + pr_debug("Failed to create and add attributes\n"); 989 + goto err_destroy_classdev; 990 + } 991 + 992 + bioscfg_drv.authentication_dir_kset = kset_create_and_add("authentication", NULL, 993 + &bioscfg_drv.class_dev->kobj); 994 + if (!bioscfg_drv.authentication_dir_kset) { 995 + ret = -ENOMEM; 996 + pr_debug("Failed to create and add authentication\n"); 997 + goto err_release_attributes_data; 998 + } 999 + 1000 + /* 1001 + * sysfs level attributes. 1002 + * - pending_reboot 1003 + */ 1004 + ret = create_attributes_level_sysfs_files(); 1005 + if (ret) 1006 + pr_debug("Failed to create sysfs level attributes\n"); 1007 + 1008 + ret = hp_init_bios_attributes(HPWMI_STRING_TYPE, HP_WMI_BIOS_STRING_GUID); 1009 + if (ret) 1010 + pr_debug("Failed to populate string type attributes\n"); 1011 + 1012 + ret = hp_init_bios_attributes(HPWMI_INTEGER_TYPE, HP_WMI_BIOS_INTEGER_GUID); 1013 + if (ret) 1014 + pr_debug("Failed to populate integer type attributes\n"); 1015 + 1016 + ret = hp_init_bios_attributes(HPWMI_ENUMERATION_TYPE, HP_WMI_BIOS_ENUMERATION_GUID); 1017 + if (ret) 1018 + pr_debug("Failed to populate enumeration type attributes\n"); 1019 + 1020 + ret = hp_init_bios_attributes(HPWMI_ORDERED_LIST_TYPE, HP_WMI_BIOS_ORDERED_LIST_GUID); 1021 + if (ret) 1022 + pr_debug("Failed to populate ordered list object type attributes\n"); 1023 + 1024 + ret = hp_init_bios_attributes(HPWMI_PASSWORD_TYPE, HP_WMI_BIOS_PASSWORD_GUID); 1025 + if (ret) 1026 + pr_debug("Failed to populate password object type attributes\n"); 1027 + 1028 + bioscfg_drv.spm_data.attr_name_kobj = NULL; 1029 + ret = hp_add_other_attributes(HPWMI_SECURE_PLATFORM_TYPE); 1030 + if (ret) 1031 + pr_debug("Failed to populate secure platform object type attribute\n"); 1032 + 1033 + bioscfg_drv.sure_start_attr_kobj = NULL; 1034 + ret = hp_add_other_attributes(HPWMI_SURE_START_TYPE); 1035 + if (ret) 1036 + pr_debug("Failed to populate sure start object type attribute\n"); 1037 + 1038 + return 0; 1039 + 1040 + err_release_attributes_data: 1041 + release_attributes_data(); 1042 + 1043 + err_destroy_classdev: 1044 + device_destroy(fw_attr_class, MKDEV(0, 0)); 1045 + 1046 + err_unregister_class: 1047 + fw_attributes_class_put(); 1048 + hp_exit_attr_set_interface(); 1049 + 1050 + return ret; 1051 + } 1052 + 1053 + static void __exit hp_exit(void) 1054 + { 1055 + release_attributes_data(); 1056 + device_destroy(fw_attr_class, MKDEV(0, 0)); 1057 + 1058 + fw_attributes_class_put(); 1059 + hp_exit_attr_set_interface(); 1060 + } 1061 + 1062 + module_init(hp_init); 1063 + module_exit(hp_exit);
+487
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 2 + * 3 + * Definitions for kernel modules using hp_bioscfg driver 4 + * 5 + * Copyright (c) 2022 HP Development Company, L.P. 6 + */ 7 + 8 + #ifndef _HP_BIOSCFG_H_ 9 + #define _HP_BIOSCFG_H_ 10 + 11 + #include <linux/wmi.h> 12 + #include <linux/types.h> 13 + #include <linux/device.h> 14 + #include <linux/module.h> 15 + #include <linux/kernel.h> 16 + #include <linux/nls.h> 17 + 18 + #define DRIVER_NAME "hp-bioscfg" 19 + 20 + #define MAX_BUFF_SIZE 512 21 + #define MAX_KEY_MOD_SIZE 256 22 + #define MAX_PASSWD_SIZE 64 23 + #define MAX_PREREQUISITES_SIZE 20 24 + #define MAX_REQ_ELEM_SIZE 128 25 + #define MAX_VALUES_SIZE 16 26 + #define MAX_ENCODINGS_SIZE 16 27 + #define MAX_ELEMENTS_SIZE 16 28 + 29 + #define SPM_STR_DESC "Secure Platform Management" 30 + #define SPM_STR "SPM" 31 + #define SURE_START_DESC "Sure Start" 32 + #define SURE_START_STR "Sure_Start" 33 + #define SETUP_PASSWD "Setup Password" 34 + #define POWER_ON_PASSWD "Power-On Password" 35 + 36 + #define LANG_CODE_STR "en_US.UTF-8" 37 + #define SCHEDULE_POWER_ON "Scheduled Power-On" 38 + 39 + #define COMMA_SEP "," 40 + #define SEMICOLON_SEP ";" 41 + 42 + /* Sure Admin Functions */ 43 + 44 + #define UTF_PREFIX "<utf-16/>" 45 + #define BEAM_PREFIX "<BEAM/>" 46 + 47 + enum mechanism_values { 48 + PASSWORD = 0x00, 49 + SIGNING_KEY = 0x01, 50 + ENDORSEMENT_KEY = 0x02, 51 + }; 52 + 53 + #define BIOS_ADMIN "bios-admin" 54 + #define POWER_ON "power-on" 55 + #define BIOS_SPM "enhanced-bios-auth" 56 + 57 + #define PASSWD_MECHANISM_TYPES "password" 58 + 59 + #define HP_WMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" 60 + 61 + #define HP_WMI_BIOS_STRING_GUID "988D08E3-68F4-4c35-AF3E-6A1B8106F83C" 62 + #define HP_WMI_BIOS_INTEGER_GUID "8232DE3D-663D-4327-A8F4-E293ADB9BF05" 63 + #define HP_WMI_BIOS_ENUMERATION_GUID "2D114B49-2DFB-4130-B8FE-4A3C09E75133" 64 + #define HP_WMI_BIOS_ORDERED_LIST_GUID "14EA9746-CE1F-4098-A0E0-7045CB4DA745" 65 + #define HP_WMI_BIOS_PASSWORD_GUID "322F2028-0F84-4901-988E-015176049E2D" 66 + #define HP_WMI_SET_BIOS_SETTING_GUID "1F4C91EB-DC5C-460b-951D-C7CB9B4B8D5E" 67 + 68 + enum hp_wmi_spm_commandtype { 69 + HPWMI_SECUREPLATFORM_GET_STATE = 0x10, 70 + HPWMI_SECUREPLATFORM_SET_KEK = 0x11, 71 + HPWMI_SECUREPLATFORM_SET_SK = 0x12, 72 + }; 73 + 74 + enum hp_wmi_surestart_commandtype { 75 + HPWMI_SURESTART_GET_LOG_COUNT = 0x01, 76 + HPWMI_SURESTART_GET_LOG = 0x02, 77 + }; 78 + 79 + enum hp_wmi_command { 80 + HPWMI_READ = 0x01, 81 + HPWMI_WRITE = 0x02, 82 + HPWMI_ODM = 0x03, 83 + HPWMI_SURESTART = 0x20006, 84 + HPWMI_GM = 0x20008, 85 + HPWMI_SECUREPLATFORM = 0x20010, 86 + }; 87 + 88 + struct bios_return { 89 + u32 sigpass; 90 + u32 return_code; 91 + }; 92 + 93 + enum wmi_error_values { 94 + SUCCESS = 0x00, 95 + CMD_FAILED = 0x01, 96 + INVALID_SIGN = 0x02, 97 + INVALID_CMD_VALUE = 0x03, 98 + INVALID_CMD_TYPE = 0x04, 99 + INVALID_DATA_SIZE = 0x05, 100 + INVALID_CMD_PARAM = 0x06, 101 + ENCRYP_CMD_REQUIRED = 0x07, 102 + NO_SECURE_SESSION = 0x08, 103 + SECURE_SESSION_FOUND = 0x09, 104 + SECURE_SESSION_FAILED = 0x0A, 105 + AUTH_FAILED = 0x0B, 106 + INVALID_BIOS_AUTH = 0x0E, 107 + NONCE_DID_NOT_MATCH = 0x18, 108 + GENERIC_ERROR = 0x1C, 109 + BIOS_ADMIN_POLICY_NOT_MET = 0x28, 110 + BIOS_ADMIN_NOT_SET = 0x38, 111 + P21_NO_PROVISIONED = 0x1000, 112 + P21_PROVISION_IN_PROGRESS = 0x1001, 113 + P21_IN_USE = 0x1002, 114 + HEP_NOT_ACTIVE = 0x1004, 115 + HEP_ALREADY_SET = 0x1006, 116 + HEP_CHECK_STATE = 0x1007, 117 + }; 118 + 119 + struct common_data { 120 + u8 display_name[MAX_BUFF_SIZE]; 121 + u8 path[MAX_BUFF_SIZE]; 122 + u32 is_readonly; 123 + u32 display_in_ui; 124 + u32 requires_physical_presence; 125 + u32 sequence; 126 + u32 prerequisites_size; 127 + u8 prerequisites[MAX_PREREQUISITES_SIZE][MAX_BUFF_SIZE]; 128 + u32 security_level; 129 + }; 130 + 131 + struct string_data { 132 + struct common_data common; 133 + struct kobject *attr_name_kobj; 134 + u8 current_value[MAX_BUFF_SIZE]; 135 + u8 new_value[MAX_BUFF_SIZE]; 136 + u32 min_length; 137 + u32 max_length; 138 + }; 139 + 140 + struct integer_data { 141 + struct common_data common; 142 + struct kobject *attr_name_kobj; 143 + u32 current_value; 144 + u32 new_value; 145 + u32 lower_bound; 146 + u32 upper_bound; 147 + u32 scalar_increment; 148 + }; 149 + 150 + struct enumeration_data { 151 + struct common_data common; 152 + struct kobject *attr_name_kobj; 153 + u8 current_value[MAX_BUFF_SIZE]; 154 + u8 new_value[MAX_BUFF_SIZE]; 155 + u32 possible_values_size; 156 + u8 possible_values[MAX_VALUES_SIZE][MAX_BUFF_SIZE]; 157 + }; 158 + 159 + struct ordered_list_data { 160 + struct common_data common; 161 + struct kobject *attr_name_kobj; 162 + u8 current_value[MAX_BUFF_SIZE]; 163 + u8 new_value[MAX_BUFF_SIZE]; 164 + u32 elements_size; 165 + u8 elements[MAX_ELEMENTS_SIZE][MAX_BUFF_SIZE]; 166 + }; 167 + 168 + struct password_data { 169 + struct common_data common; 170 + struct kobject *attr_name_kobj; 171 + u8 current_password[MAX_PASSWD_SIZE]; 172 + u8 new_password[MAX_PASSWD_SIZE]; 173 + u32 min_password_length; 174 + u32 max_password_length; 175 + u32 encodings_size; 176 + u8 encodings[MAX_ENCODINGS_SIZE][MAX_BUFF_SIZE]; 177 + bool is_enabled; 178 + 179 + /* 180 + * 'role' identifies the type of authentication. 181 + * Two known types are bios-admin and power-on. 182 + * 'bios-admin' represents BIOS administrator password 183 + * 'power-on' represents a password required to use the system 184 + */ 185 + u32 role; 186 + 187 + /* 188 + * 'mechanism' represents the means of authentication. 189 + * Only supported type currently is "password" 190 + */ 191 + u32 mechanism; 192 + }; 193 + 194 + struct secure_platform_data { 195 + struct kobject *attr_name_kobj; 196 + u8 attribute_name[MAX_BUFF_SIZE]; 197 + u8 *endorsement_key; 198 + u8 *signing_key; 199 + u8 *auth_token; 200 + bool is_enabled; 201 + u32 mechanism; 202 + }; 203 + 204 + struct bioscfg_priv { 205 + struct kset *authentication_dir_kset; 206 + struct kset *main_dir_kset; 207 + struct device *class_dev; 208 + struct string_data *string_data; 209 + u32 string_instances_count; 210 + struct integer_data *integer_data; 211 + u32 integer_instances_count; 212 + struct enumeration_data *enumeration_data; 213 + u32 enumeration_instances_count; 214 + struct ordered_list_data *ordered_list_data; 215 + u32 ordered_list_instances_count; 216 + struct password_data *password_data; 217 + u32 password_instances_count; 218 + 219 + struct kobject *sure_start_attr_kobj; 220 + struct secure_platform_data spm_data; 221 + u8 display_name_language_code[MAX_BUFF_SIZE]; 222 + bool pending_reboot; 223 + struct mutex mutex; 224 + }; 225 + 226 + /* global structure used by multiple WMI interfaces */ 227 + extern struct bioscfg_priv bioscfg_drv; 228 + 229 + enum hp_wmi_data_type { 230 + HPWMI_STRING_TYPE, 231 + HPWMI_INTEGER_TYPE, 232 + HPWMI_ENUMERATION_TYPE, 233 + HPWMI_ORDERED_LIST_TYPE, 234 + HPWMI_PASSWORD_TYPE, 235 + HPWMI_SECURE_PLATFORM_TYPE, 236 + HPWMI_SURE_START_TYPE, 237 + }; 238 + 239 + enum hp_wmi_data_elements { 240 + /* Common elements */ 241 + NAME = 0, 242 + VALUE = 1, 243 + PATH = 2, 244 + IS_READONLY = 3, 245 + DISPLAY_IN_UI = 4, 246 + REQUIRES_PHYSICAL_PRESENCE = 5, 247 + SEQUENCE = 6, 248 + PREREQUISITES_SIZE = 7, 249 + PREREQUISITES = 8, 250 + SECURITY_LEVEL = 9, 251 + 252 + /* String elements */ 253 + STR_MIN_LENGTH = 10, 254 + STR_MAX_LENGTH = 11, 255 + STR_ELEM_CNT = 12, 256 + 257 + /* Integer elements */ 258 + INT_LOWER_BOUND = 10, 259 + INT_UPPER_BOUND = 11, 260 + INT_SCALAR_INCREMENT = 12, 261 + INT_ELEM_CNT = 13, 262 + 263 + /* Enumeration elements */ 264 + ENUM_CURRENT_VALUE = 10, 265 + ENUM_SIZE = 11, 266 + ENUM_POSSIBLE_VALUES = 12, 267 + ENUM_ELEM_CNT = 13, 268 + 269 + /* Ordered list elements */ 270 + ORD_LIST_SIZE = 10, 271 + ORD_LIST_ELEMENTS = 11, 272 + ORD_ELEM_CNT = 12, 273 + 274 + /* Password elements */ 275 + PSWD_MIN_LENGTH = 10, 276 + PSWD_MAX_LENGTH = 11, 277 + PSWD_SIZE = 12, 278 + PSWD_ENCODINGS = 13, 279 + PSWD_IS_SET = 14, 280 + PSWD_ELEM_CNT = 15, 281 + }; 282 + 283 + #define GET_INSTANCE_ID(type) \ 284 + static int get_##type##_instance_id(struct kobject *kobj) \ 285 + { \ 286 + int i; \ 287 + \ 288 + for (i = 0; i <= bioscfg_drv.type##_instances_count; i++) { \ 289 + if (!strcmp(kobj->name, bioscfg_drv.type##_data[i].attr_name_kobj->name)) \ 290 + return i; \ 291 + } \ 292 + return -EIO; \ 293 + } 294 + 295 + #define ATTRIBUTE_S_PROPERTY_SHOW(name, type) \ 296 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ 297 + char *buf) \ 298 + { \ 299 + int i = get_##type##_instance_id(kobj); \ 300 + if (i >= 0) \ 301 + return sysfs_emit(buf, "%s\n", bioscfg_drv.type##_data[i].name); \ 302 + return -EIO; \ 303 + } 304 + 305 + #define ATTRIBUTE_N_PROPERTY_SHOW(name, type) \ 306 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ 307 + char *buf) \ 308 + { \ 309 + int i = get_##type##_instance_id(kobj); \ 310 + if (i >= 0) \ 311 + return sysfs_emit(buf, "%d\n", bioscfg_drv.type##_data[i].name); \ 312 + return -EIO; \ 313 + } 314 + 315 + #define ATTRIBUTE_PROPERTY_STORE(curr_val, type) \ 316 + static ssize_t curr_val##_store(struct kobject *kobj, \ 317 + struct kobj_attribute *attr, \ 318 + const char *buf, size_t count) \ 319 + { \ 320 + char *attr_value = NULL; \ 321 + int i; \ 322 + int ret = -EIO; \ 323 + \ 324 + attr_value = kstrdup(buf, GFP_KERNEL); \ 325 + if (!attr_value) \ 326 + return -ENOMEM; \ 327 + \ 328 + ret = hp_enforce_single_line_input(attr_value, count); \ 329 + if (!ret) { \ 330 + i = get_##type##_instance_id(kobj); \ 331 + if (i >= 0) \ 332 + ret = validate_##type##_input(i, attr_value); \ 333 + } \ 334 + if (!ret) \ 335 + ret = hp_set_attribute(kobj->name, attr_value); \ 336 + if (!ret) { \ 337 + update_##type##_value(i, attr_value); \ 338 + if (bioscfg_drv.type##_data[i].common.requires_physical_presence) \ 339 + hp_set_reboot_and_signal_event(); \ 340 + } \ 341 + hp_clear_all_credentials(); \ 342 + kfree(attr_value); \ 343 + \ 344 + return ret ? ret : count; \ 345 + } 346 + 347 + #define ATTRIBUTE_SPM_N_PROPERTY_SHOW(name, type) \ 348 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ 349 + { \ 350 + return sysfs_emit(buf, "%d\n", bioscfg_drv.type##_data.name); \ 351 + } 352 + 353 + #define ATTRIBUTE_SPM_S_PROPERTY_SHOW(name, type) \ 354 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) \ 355 + { \ 356 + return sysfs_emit(buf, "%s\n", bioscfg_drv.type##_data.name); \ 357 + } 358 + 359 + #define ATTRIBUTE_VALUES_PROPERTY_SHOW(name, type, sep) \ 360 + static ssize_t name##_show(struct kobject *kobj, \ 361 + struct kobj_attribute *attr, char *buf) \ 362 + { \ 363 + int i; \ 364 + int len = 0; \ 365 + int instance_id = get_##type##_instance_id(kobj); \ 366 + \ 367 + if (instance_id < 0) \ 368 + return 0; \ 369 + \ 370 + for (i = 0; i < bioscfg_drv.type##_data[instance_id].name##_size; i++) { \ 371 + if (i) \ 372 + len += sysfs_emit_at(buf, len, "%s", sep); \ 373 + \ 374 + len += sysfs_emit_at(buf, len, "%s", \ 375 + bioscfg_drv.type##_data[instance_id].name[i]); \ 376 + } \ 377 + len += sysfs_emit_at(buf, len, "\n"); \ 378 + return len; \ 379 + } 380 + 381 + #define ATTRIBUTE_S_COMMON_PROPERTY_SHOW(name, type) \ 382 + static ssize_t name##_show(struct kobject *kobj, struct kobj_attribute *attr, \ 383 + char *buf) \ 384 + { \ 385 + int i = get_##type##_instance_id(kobj); \ 386 + if (i >= 0) \ 387 + return sysfs_emit(buf, "%s\n", bioscfg_drv.type##_data[i].common.name); \ 388 + return -EIO; \ 389 + } 390 + 391 + extern struct kobj_attribute common_display_langcode; 392 + 393 + /* Prototypes */ 394 + 395 + /* String attributes */ 396 + int hp_populate_string_buffer_data(u8 *buffer_ptr, u32 *buffer_size, 397 + int instance_id, 398 + struct kobject *attr_name_kobj); 399 + int hp_alloc_string_data(void); 400 + void hp_exit_string_attributes(void); 401 + int hp_populate_string_package_data(union acpi_object *str_obj, 402 + int instance_id, 403 + struct kobject *attr_name_kobj); 404 + 405 + /* Integer attributes */ 406 + int hp_populate_integer_buffer_data(u8 *buffer_ptr, u32 *buffer_size, 407 + int instance_id, 408 + struct kobject *attr_name_kobj); 409 + int hp_alloc_integer_data(void); 410 + void hp_exit_integer_attributes(void); 411 + int hp_populate_integer_package_data(union acpi_object *integer_obj, 412 + int instance_id, 413 + struct kobject *attr_name_kobj); 414 + 415 + /* Enumeration attributes */ 416 + int hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size, 417 + int instance_id, 418 + struct kobject *attr_name_kobj); 419 + int hp_alloc_enumeration_data(void); 420 + void hp_exit_enumeration_attributes(void); 421 + int hp_populate_enumeration_package_data(union acpi_object *enum_obj, 422 + int instance_id, 423 + struct kobject *attr_name_kobj); 424 + 425 + /* Ordered list */ 426 + int hp_populate_ordered_list_buffer_data(u8 *buffer_ptr, 427 + u32 *buffer_size, 428 + int instance_id, 429 + struct kobject *attr_name_kobj); 430 + int hp_alloc_ordered_list_data(void); 431 + void hp_exit_ordered_list_attributes(void); 432 + int hp_populate_ordered_list_package_data(union acpi_object *order_obj, 433 + int instance_id, 434 + struct kobject *attr_name_kobj); 435 + 436 + /* Password authentication attributes */ 437 + int hp_populate_password_buffer_data(u8 *buffer_ptr, u32 *buffer_size, 438 + int instance_id, 439 + struct kobject *attr_name_kobj); 440 + int hp_populate_password_package_data(union acpi_object *password_obj, 441 + int instance_id, 442 + struct kobject *attr_name_kobj); 443 + int hp_alloc_password_data(void); 444 + int hp_get_password_instance_for_type(const char *name); 445 + int hp_clear_all_credentials(void); 446 + int hp_set_attribute(const char *a_name, const char *a_value); 447 + 448 + /* SPM attributes */ 449 + void hp_exit_password_attributes(void); 450 + void hp_exit_secure_platform_attributes(void); 451 + int hp_populate_secure_platform_data(struct kobject *attr_name_kobj); 452 + int hp_populate_security_buffer(u16 *buffer, const char *authentication); 453 + 454 + /* Bios Attributes interface */ 455 + int hp_wmi_set_bios_setting(u16 *input_buffer, u32 input_size); 456 + int hp_wmi_perform_query(int query, enum hp_wmi_command command, 457 + void *buffer, u32 insize, u32 outsize); 458 + 459 + /* Sure Start attributes */ 460 + void hp_exit_sure_start_attributes(void); 461 + int hp_populate_sure_start_data(struct kobject *attr_name_kobj); 462 + 463 + /* Bioscfg */ 464 + 465 + void hp_exit_attr_set_interface(void); 466 + int hp_init_attr_set_interface(void); 467 + size_t hp_calculate_string_buffer(const char *str); 468 + size_t hp_calculate_security_buffer(const char *authentication); 469 + void *hp_ascii_to_utf16_unicode(u16 *p, const u8 *str); 470 + int hp_get_integer_from_buffer(u8 **buffer, u32 *buffer_size, u32 *integer); 471 + int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_size); 472 + int hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int *len); 473 + int hp_encode_outsize_for_pvsz(int outsize); 474 + int hp_enforce_single_line_input(char *buf, size_t count); 475 + void hp_set_reboot_and_signal_event(void); 476 + ssize_t display_name_language_code_show(struct kobject *kobj, 477 + struct kobj_attribute *attr, 478 + char *buf); 479 + union acpi_object *hp_get_wmiobj_pointer(int instance_id, const char *guid_string); 480 + int hp_get_instance_count(const char *guid_string); 481 + void hp_update_attribute_permissions(bool isreadonly, struct kobj_attribute *current_val); 482 + void hp_friendly_user_name_update(char *path, const char *attr_name, 483 + char *attr_display, int attr_size); 484 + int hp_wmi_error_and_message(int error_code); 485 + int hp_get_common_data_from_buffer(u8 **buffer_ptr, u32 *buffer_size, struct common_data *common); 486 + 487 + #endif
+457
drivers/platform/x86/hp/hp-bioscfg/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 hp-bioscfg driver. 5 + * 6 + * Copyright (c) 2022 HP Development Company, L.P. 7 + */ 8 + 9 + #include "bioscfg.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 + 17 + if (instance_id < 0) 18 + return -EIO; 19 + 20 + return sysfs_emit(buf, "%s\n", 21 + bioscfg_drv.enumeration_data[instance_id].current_value); 22 + } 23 + 24 + /** 25 + * validate_enumeration_input() - 26 + * Validate input of current_value against possible values 27 + * 28 + * @instance_id: The instance on which input is validated 29 + * @buf: Input value 30 + */ 31 + static int validate_enumeration_input(int instance_id, const char *buf) 32 + { 33 + int i; 34 + int found = 0; 35 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 36 + 37 + /* Is it a read only attribute */ 38 + if (enum_data->common.is_readonly) 39 + return -EIO; 40 + 41 + for (i = 0; i < enum_data->possible_values_size && !found; i++) 42 + if (!strcmp(enum_data->possible_values[i], buf)) 43 + found = 1; 44 + 45 + if (!found) 46 + return -EINVAL; 47 + 48 + return 0; 49 + } 50 + 51 + static void update_enumeration_value(int instance_id, char *attr_value) 52 + { 53 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 54 + 55 + strscpy(enum_data->current_value, 56 + attr_value, 57 + sizeof(enum_data->current_value)); 58 + } 59 + 60 + ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, enumeration); 61 + static struct kobj_attribute enumeration_display_name = 62 + __ATTR_RO(display_name); 63 + 64 + ATTRIBUTE_PROPERTY_STORE(current_value, enumeration); 65 + static struct kobj_attribute enumeration_current_val = 66 + __ATTR_RW(current_value); 67 + 68 + ATTRIBUTE_VALUES_PROPERTY_SHOW(possible_values, enumeration, SEMICOLON_SEP); 69 + static struct kobj_attribute enumeration_poss_val = 70 + __ATTR_RO(possible_values); 71 + 72 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 73 + char *buf) 74 + { 75 + return sysfs_emit(buf, "enumeration\n"); 76 + } 77 + 78 + static struct kobj_attribute enumeration_type = 79 + __ATTR_RO(type); 80 + 81 + static struct attribute *enumeration_attrs[] = { 82 + &common_display_langcode.attr, 83 + &enumeration_display_name.attr, 84 + &enumeration_current_val.attr, 85 + &enumeration_poss_val.attr, 86 + &enumeration_type.attr, 87 + NULL 88 + }; 89 + 90 + static const struct attribute_group enumeration_attr_group = { 91 + .attrs = enumeration_attrs, 92 + }; 93 + 94 + int hp_alloc_enumeration_data(void) 95 + { 96 + bioscfg_drv.enumeration_instances_count = 97 + hp_get_instance_count(HP_WMI_BIOS_ENUMERATION_GUID); 98 + 99 + bioscfg_drv.enumeration_data = kcalloc(bioscfg_drv.enumeration_instances_count, 100 + sizeof(*bioscfg_drv.enumeration_data), GFP_KERNEL); 101 + if (!bioscfg_drv.enumeration_data) { 102 + bioscfg_drv.enumeration_instances_count = 0; 103 + return -ENOMEM; 104 + } 105 + return 0; 106 + } 107 + 108 + /* Expected Values types associated with each element */ 109 + static const acpi_object_type expected_enum_types[] = { 110 + [NAME] = ACPI_TYPE_STRING, 111 + [VALUE] = ACPI_TYPE_STRING, 112 + [PATH] = ACPI_TYPE_STRING, 113 + [IS_READONLY] = ACPI_TYPE_INTEGER, 114 + [DISPLAY_IN_UI] = ACPI_TYPE_INTEGER, 115 + [REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER, 116 + [SEQUENCE] = ACPI_TYPE_INTEGER, 117 + [PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER, 118 + [PREREQUISITES] = ACPI_TYPE_STRING, 119 + [SECURITY_LEVEL] = ACPI_TYPE_INTEGER, 120 + [ENUM_CURRENT_VALUE] = ACPI_TYPE_STRING, 121 + [ENUM_SIZE] = ACPI_TYPE_INTEGER, 122 + [ENUM_POSSIBLE_VALUES] = ACPI_TYPE_STRING, 123 + }; 124 + 125 + static int hp_populate_enumeration_elements_from_package(union acpi_object *enum_obj, 126 + int enum_obj_count, 127 + int instance_id) 128 + { 129 + char *str_value = NULL; 130 + int value_len; 131 + u32 size = 0; 132 + u32 int_value = 0; 133 + int elem = 0; 134 + int reqs; 135 + int pos_values; 136 + int ret; 137 + int eloc; 138 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 139 + 140 + for (elem = 1, eloc = 1; elem < enum_obj_count; elem++, eloc++) { 141 + /* ONLY look at the first ENUM_ELEM_CNT elements */ 142 + if (eloc == ENUM_ELEM_CNT) 143 + goto exit_enumeration_package; 144 + 145 + switch (enum_obj[elem].type) { 146 + case ACPI_TYPE_STRING: 147 + if (PREREQUISITES != elem && ENUM_POSSIBLE_VALUES != elem) { 148 + ret = hp_convert_hexstr_to_str(enum_obj[elem].string.pointer, 149 + enum_obj[elem].string.length, 150 + &str_value, &value_len); 151 + if (ret) 152 + return -EINVAL; 153 + } 154 + break; 155 + case ACPI_TYPE_INTEGER: 156 + int_value = (u32)enum_obj[elem].integer.value; 157 + break; 158 + default: 159 + pr_warn("Unsupported object type [%d]\n", enum_obj[elem].type); 160 + continue; 161 + } 162 + 163 + /* Check that both expected and read object type match */ 164 + if (expected_enum_types[eloc] != enum_obj[elem].type) { 165 + pr_err("Error expected type %d for elem %d, but got type %d instead\n", 166 + expected_enum_types[eloc], elem, enum_obj[elem].type); 167 + kfree(str_value); 168 + return -EIO; 169 + } 170 + 171 + /* Assign appropriate element value to corresponding field */ 172 + switch (eloc) { 173 + case NAME: 174 + case VALUE: 175 + break; 176 + case PATH: 177 + strscpy(enum_data->common.path, str_value, 178 + sizeof(enum_data->common.path)); 179 + break; 180 + case IS_READONLY: 181 + enum_data->common.is_readonly = int_value; 182 + break; 183 + case DISPLAY_IN_UI: 184 + enum_data->common.display_in_ui = int_value; 185 + break; 186 + case REQUIRES_PHYSICAL_PRESENCE: 187 + enum_data->common.requires_physical_presence = int_value; 188 + break; 189 + case SEQUENCE: 190 + enum_data->common.sequence = int_value; 191 + break; 192 + case PREREQUISITES_SIZE: 193 + if (int_value > MAX_PREREQUISITES_SIZE) { 194 + pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n"); 195 + int_value = MAX_PREREQUISITES_SIZE; 196 + } 197 + enum_data->common.prerequisites_size = int_value; 198 + 199 + /* 200 + * This step is needed to keep the expected 201 + * element list pointing to the right obj[elem].type 202 + * when the size is zero. PREREQUISITES 203 + * object is omitted by BIOS when the size is 204 + * zero. 205 + */ 206 + if (int_value == 0) 207 + eloc++; 208 + break; 209 + 210 + case PREREQUISITES: 211 + size = min_t(u32, enum_data->common.prerequisites_size, MAX_PREREQUISITES_SIZE); 212 + for (reqs = 0; reqs < size; reqs++) { 213 + if (elem >= enum_obj_count) { 214 + pr_err("Error enum-objects package is too small\n"); 215 + return -EINVAL; 216 + } 217 + 218 + ret = hp_convert_hexstr_to_str(enum_obj[elem + reqs].string.pointer, 219 + enum_obj[elem + reqs].string.length, 220 + &str_value, &value_len); 221 + 222 + if (ret) 223 + return -EINVAL; 224 + 225 + strscpy(enum_data->common.prerequisites[reqs], 226 + str_value, 227 + sizeof(enum_data->common.prerequisites[reqs])); 228 + 229 + kfree(str_value); 230 + str_value = NULL; 231 + } 232 + break; 233 + 234 + case SECURITY_LEVEL: 235 + enum_data->common.security_level = int_value; 236 + break; 237 + 238 + case ENUM_CURRENT_VALUE: 239 + strscpy(enum_data->current_value, 240 + str_value, sizeof(enum_data->current_value)); 241 + break; 242 + case ENUM_SIZE: 243 + if (int_value > MAX_VALUES_SIZE) { 244 + pr_warn("Possible number values size value exceeded the maximum number of elements supported or data may be malformed\n"); 245 + int_value = MAX_VALUES_SIZE; 246 + } 247 + enum_data->possible_values_size = int_value; 248 + 249 + /* 250 + * This step is needed to keep the expected 251 + * element list pointing to the right obj[elem].type 252 + * when the size is zero. POSSIBLE_VALUES 253 + * object is omitted by BIOS when the size is zero. 254 + */ 255 + if (int_value == 0) 256 + eloc++; 257 + break; 258 + 259 + case ENUM_POSSIBLE_VALUES: 260 + size = enum_data->possible_values_size; 261 + 262 + for (pos_values = 0; pos_values < size && pos_values < MAX_VALUES_SIZE; 263 + pos_values++) { 264 + if (elem >= enum_obj_count) { 265 + pr_err("Error enum-objects package is too small\n"); 266 + return -EINVAL; 267 + } 268 + 269 + ret = hp_convert_hexstr_to_str(enum_obj[elem + pos_values].string.pointer, 270 + enum_obj[elem + pos_values].string.length, 271 + &str_value, &value_len); 272 + 273 + if (ret) 274 + return -EINVAL; 275 + 276 + /* 277 + * ignore strings when possible values size 278 + * is greater than MAX_VALUES_SIZE 279 + */ 280 + if (size < MAX_VALUES_SIZE) 281 + strscpy(enum_data->possible_values[pos_values], 282 + str_value, 283 + sizeof(enum_data->possible_values[pos_values])); 284 + 285 + kfree(str_value); 286 + str_value = NULL; 287 + } 288 + break; 289 + default: 290 + pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem); 291 + break; 292 + } 293 + 294 + kfree(str_value); 295 + str_value = NULL; 296 + } 297 + 298 + exit_enumeration_package: 299 + kfree(str_value); 300 + return 0; 301 + } 302 + 303 + /** 304 + * hp_populate_enumeration_package_data() - 305 + * Populate all properties of an instance under enumeration attribute 306 + * 307 + * @enum_obj: ACPI object with enumeration data 308 + * @instance_id: The instance to enumerate 309 + * @attr_name_kobj: The parent kernel object 310 + */ 311 + int hp_populate_enumeration_package_data(union acpi_object *enum_obj, 312 + int instance_id, 313 + struct kobject *attr_name_kobj) 314 + { 315 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 316 + 317 + enum_data->attr_name_kobj = attr_name_kobj; 318 + 319 + hp_populate_enumeration_elements_from_package(enum_obj, 320 + enum_obj->package.count, 321 + instance_id); 322 + hp_update_attribute_permissions(enum_data->common.is_readonly, 323 + &enumeration_current_val); 324 + /* 325 + * Several attributes have names such "MONDAY". Friendly 326 + * user nane is generated to make the name more descriptive 327 + */ 328 + hp_friendly_user_name_update(enum_data->common.path, 329 + attr_name_kobj->name, 330 + enum_data->common.display_name, 331 + sizeof(enum_data->common.display_name)); 332 + return sysfs_create_group(attr_name_kobj, &enumeration_attr_group); 333 + } 334 + 335 + static int hp_populate_enumeration_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size, 336 + int instance_id) 337 + { 338 + int values; 339 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 340 + int ret = 0; 341 + 342 + /* 343 + * Only data relevant to this driver and its functionality is 344 + * read. BIOS defines the order in which each * element is 345 + * read. Element 0 data is not relevant to this 346 + * driver hence it is ignored. For clarity, all element names 347 + * (DISPLAY_IN_UI) which defines the order in which is read 348 + * and the name matches the variable where the data is stored. 349 + * 350 + * In earlier implementation, reported errors were ignored 351 + * causing the data to remain uninitialized. It is not 352 + * possible to determine if data read from BIOS is valid or 353 + * not. It is for this reason functions may return a error 354 + * without validating the data itself. 355 + */ 356 + 357 + // VALUE: 358 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, enum_data->current_value, 359 + sizeof(enum_data->current_value)); 360 + if (ret < 0) 361 + goto buffer_exit; 362 + 363 + // COMMON: 364 + ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, &enum_data->common); 365 + if (ret < 0) 366 + goto buffer_exit; 367 + 368 + // ENUM_CURRENT_VALUE: 369 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, 370 + enum_data->current_value, 371 + sizeof(enum_data->current_value)); 372 + if (ret < 0) 373 + goto buffer_exit; 374 + 375 + // ENUM_SIZE: 376 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 377 + &enum_data->possible_values_size); 378 + 379 + if (enum_data->possible_values_size > MAX_VALUES_SIZE) { 380 + /* Report a message and limit possible values size to maximum value */ 381 + pr_warn("Enum Possible size value exceeded the maximum number of elements supported or data may be malformed\n"); 382 + enum_data->possible_values_size = MAX_VALUES_SIZE; 383 + } 384 + 385 + // ENUM_POSSIBLE_VALUES: 386 + for (values = 0; values < enum_data->possible_values_size; values++) { 387 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, 388 + enum_data->possible_values[values], 389 + sizeof(enum_data->possible_values[values])); 390 + if (ret < 0) 391 + break; 392 + } 393 + 394 + buffer_exit: 395 + return ret; 396 + } 397 + 398 + /** 399 + * hp_populate_enumeration_buffer_data() - 400 + * Populate all properties of an instance under enumeration attribute 401 + * 402 + * @buffer_ptr: Buffer pointer 403 + * @buffer_size: Buffer size 404 + * @instance_id: The instance to enumerate 405 + * @attr_name_kobj: The parent kernel object 406 + */ 407 + int hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size, 408 + int instance_id, 409 + struct kobject *attr_name_kobj) 410 + { 411 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 412 + int ret = 0; 413 + 414 + enum_data->attr_name_kobj = attr_name_kobj; 415 + 416 + /* Populate enumeration elements */ 417 + ret = hp_populate_enumeration_elements_from_buffer(buffer_ptr, buffer_size, 418 + instance_id); 419 + if (ret < 0) 420 + return ret; 421 + 422 + hp_update_attribute_permissions(enum_data->common.is_readonly, 423 + &enumeration_current_val); 424 + /* 425 + * Several attributes have names such "MONDAY". A Friendlier 426 + * user nane is generated to make the name more descriptive 427 + */ 428 + hp_friendly_user_name_update(enum_data->common.path, 429 + attr_name_kobj->name, 430 + enum_data->common.display_name, 431 + sizeof(enum_data->common.display_name)); 432 + 433 + return sysfs_create_group(attr_name_kobj, &enumeration_attr_group); 434 + } 435 + 436 + /** 437 + * hp_exit_enumeration_attributes() - Clear all attribute data 438 + * 439 + * Clears all data allocated for this group of attributes 440 + */ 441 + void hp_exit_enumeration_attributes(void) 442 + { 443 + int instance_id; 444 + 445 + for (instance_id = 0; instance_id < bioscfg_drv.enumeration_instances_count; 446 + instance_id++) { 447 + struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; 448 + struct kobject *attr_name_kobj = enum_data->attr_name_kobj; 449 + 450 + if (attr_name_kobj) 451 + sysfs_remove_group(attr_name_kobj, &enumeration_attr_group); 452 + } 453 + bioscfg_drv.enumeration_instances_count = 0; 454 + 455 + kfree(bioscfg_drv.enumeration_data); 456 + bioscfg_drv.enumeration_data = NULL; 457 + }
+418
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to integer type attributes under 4 + * BIOS Enumeration GUID for use with hp-bioscfg driver. 5 + * 6 + * Copyright (c) 2022 Hewlett-Packard Inc. 7 + */ 8 + 9 + #include "bioscfg.h" 10 + 11 + GET_INSTANCE_ID(integer); 12 + 13 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 14 + { 15 + int instance_id = get_integer_instance_id(kobj); 16 + 17 + if (instance_id < 0) 18 + return -EIO; 19 + 20 + return sysfs_emit(buf, "%d\n", 21 + bioscfg_drv.integer_data[instance_id].current_value); 22 + } 23 + 24 + /** 25 + * validate_integer_input() - 26 + * Validate input of current_value against lower and upper bound 27 + * 28 + * @instance_id: The instance on which input is validated 29 + * @buf: Input value 30 + */ 31 + static int validate_integer_input(int instance_id, char *buf) 32 + { 33 + int in_val; 34 + int ret; 35 + struct integer_data *integer_data = &bioscfg_drv.integer_data[instance_id]; 36 + 37 + /* BIOS treats it as a read only attribute */ 38 + if (integer_data->common.is_readonly) 39 + return -EIO; 40 + 41 + ret = kstrtoint(buf, 10, &in_val); 42 + if (ret < 0) 43 + return ret; 44 + 45 + if (in_val < integer_data->lower_bound || 46 + in_val > integer_data->upper_bound) 47 + return -ERANGE; 48 + 49 + return 0; 50 + } 51 + 52 + static void update_integer_value(int instance_id, char *attr_value) 53 + { 54 + int in_val; 55 + int ret; 56 + struct integer_data *integer_data = &bioscfg_drv.integer_data[instance_id]; 57 + 58 + ret = kstrtoint(attr_value, 10, &in_val); 59 + if (ret == 0) 60 + integer_data->current_value = in_val; 61 + else 62 + pr_warn("Invalid integer value found: %s\n", attr_value); 63 + } 64 + 65 + ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, integer); 66 + static struct kobj_attribute integer_display_name = 67 + __ATTR_RO(display_name); 68 + 69 + ATTRIBUTE_PROPERTY_STORE(current_value, integer); 70 + static struct kobj_attribute integer_current_val = 71 + __ATTR_RW_MODE(current_value, 0644); 72 + 73 + ATTRIBUTE_N_PROPERTY_SHOW(lower_bound, integer); 74 + static struct kobj_attribute integer_lower_bound = 75 + __ATTR_RO(lower_bound); 76 + 77 + ATTRIBUTE_N_PROPERTY_SHOW(upper_bound, integer); 78 + static struct kobj_attribute integer_upper_bound = 79 + __ATTR_RO(upper_bound); 80 + 81 + ATTRIBUTE_N_PROPERTY_SHOW(scalar_increment, integer); 82 + static struct kobj_attribute integer_scalar_increment = 83 + __ATTR_RO(scalar_increment); 84 + 85 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 86 + char *buf) 87 + { 88 + return sysfs_emit(buf, "integer\n"); 89 + } 90 + 91 + static struct kobj_attribute integer_type = 92 + __ATTR_RO(type); 93 + 94 + static struct attribute *integer_attrs[] = { 95 + &common_display_langcode.attr, 96 + &integer_display_name.attr, 97 + &integer_current_val.attr, 98 + &integer_lower_bound.attr, 99 + &integer_upper_bound.attr, 100 + &integer_scalar_increment.attr, 101 + &integer_type.attr, 102 + NULL 103 + }; 104 + 105 + static const struct attribute_group integer_attr_group = { 106 + .attrs = integer_attrs, 107 + }; 108 + 109 + int hp_alloc_integer_data(void) 110 + { 111 + bioscfg_drv.integer_instances_count = hp_get_instance_count(HP_WMI_BIOS_INTEGER_GUID); 112 + bioscfg_drv.integer_data = kcalloc(bioscfg_drv.integer_instances_count, 113 + sizeof(*bioscfg_drv.integer_data), GFP_KERNEL); 114 + 115 + if (!bioscfg_drv.integer_data) { 116 + bioscfg_drv.integer_instances_count = 0; 117 + return -ENOMEM; 118 + } 119 + return 0; 120 + } 121 + 122 + /* Expected Values types associated with each element */ 123 + static const acpi_object_type expected_integer_types[] = { 124 + [NAME] = ACPI_TYPE_STRING, 125 + [VALUE] = ACPI_TYPE_STRING, 126 + [PATH] = ACPI_TYPE_STRING, 127 + [IS_READONLY] = ACPI_TYPE_INTEGER, 128 + [DISPLAY_IN_UI] = ACPI_TYPE_INTEGER, 129 + [REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER, 130 + [SEQUENCE] = ACPI_TYPE_INTEGER, 131 + [PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER, 132 + [PREREQUISITES] = ACPI_TYPE_STRING, 133 + [SECURITY_LEVEL] = ACPI_TYPE_INTEGER, 134 + [INT_LOWER_BOUND] = ACPI_TYPE_INTEGER, 135 + [INT_UPPER_BOUND] = ACPI_TYPE_INTEGER, 136 + [INT_SCALAR_INCREMENT] = ACPI_TYPE_INTEGER, 137 + }; 138 + 139 + static int hp_populate_integer_elements_from_package(union acpi_object *integer_obj, 140 + int integer_obj_count, 141 + int instance_id) 142 + { 143 + char *str_value = NULL; 144 + int value_len; 145 + int ret; 146 + u32 int_value = 0; 147 + int elem; 148 + int reqs; 149 + int eloc; 150 + int size; 151 + struct integer_data *integer_data = &bioscfg_drv.integer_data[instance_id]; 152 + 153 + if (!integer_obj) 154 + return -EINVAL; 155 + 156 + for (elem = 1, eloc = 1; elem < integer_obj_count; elem++, eloc++) { 157 + /* ONLY look at the first INTEGER_ELEM_CNT elements */ 158 + if (eloc == INT_ELEM_CNT) 159 + goto exit_integer_package; 160 + 161 + switch (integer_obj[elem].type) { 162 + case ACPI_TYPE_STRING: 163 + if (elem != PREREQUISITES) { 164 + ret = hp_convert_hexstr_to_str(integer_obj[elem].string.pointer, 165 + integer_obj[elem].string.length, 166 + &str_value, &value_len); 167 + if (ret) 168 + continue; 169 + } 170 + break; 171 + case ACPI_TYPE_INTEGER: 172 + int_value = (u32)integer_obj[elem].integer.value; 173 + break; 174 + default: 175 + pr_warn("Unsupported object type [%d]\n", integer_obj[elem].type); 176 + continue; 177 + } 178 + /* Check that both expected and read object type match */ 179 + if (expected_integer_types[eloc] != integer_obj[elem].type) { 180 + pr_err("Error expected type %d for elem %d, but got type %d instead\n", 181 + expected_integer_types[eloc], elem, integer_obj[elem].type); 182 + kfree(str_value); 183 + return -EIO; 184 + } 185 + /* Assign appropriate element value to corresponding field*/ 186 + switch (eloc) { 187 + case VALUE: 188 + ret = kstrtoint(str_value, 10, &int_value); 189 + if (ret) 190 + continue; 191 + 192 + integer_data->current_value = int_value; 193 + break; 194 + case PATH: 195 + strscpy(integer_data->common.path, str_value, 196 + sizeof(integer_data->common.path)); 197 + break; 198 + case IS_READONLY: 199 + integer_data->common.is_readonly = int_value; 200 + break; 201 + case DISPLAY_IN_UI: 202 + integer_data->common.display_in_ui = int_value; 203 + break; 204 + case REQUIRES_PHYSICAL_PRESENCE: 205 + integer_data->common.requires_physical_presence = int_value; 206 + break; 207 + case SEQUENCE: 208 + integer_data->common.sequence = int_value; 209 + break; 210 + case PREREQUISITES_SIZE: 211 + if (int_value > MAX_PREREQUISITES_SIZE) { 212 + pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n"); 213 + int_value = MAX_PREREQUISITES_SIZE; 214 + } 215 + integer_data->common.prerequisites_size = int_value; 216 + 217 + /* 218 + * This step is needed to keep the expected 219 + * element list pointing to the right obj[elem].type 220 + * when the size is zero. PREREQUISITES 221 + * object is omitted by BIOS when the size is 222 + * zero. 223 + */ 224 + if (integer_data->common.prerequisites_size == 0) 225 + eloc++; 226 + break; 227 + case PREREQUISITES: 228 + size = min_t(u32, integer_data->common.prerequisites_size, MAX_PREREQUISITES_SIZE); 229 + 230 + for (reqs = 0; reqs < size; reqs++) { 231 + if (elem >= integer_obj_count) { 232 + pr_err("Error elem-objects package is too small\n"); 233 + return -EINVAL; 234 + } 235 + 236 + ret = hp_convert_hexstr_to_str(integer_obj[elem + reqs].string.pointer, 237 + integer_obj[elem + reqs].string.length, 238 + &str_value, &value_len); 239 + 240 + if (ret) 241 + continue; 242 + 243 + strscpy(integer_data->common.prerequisites[reqs], 244 + str_value, 245 + sizeof(integer_data->common.prerequisites[reqs])); 246 + kfree(str_value); 247 + str_value = NULL; 248 + } 249 + break; 250 + 251 + case SECURITY_LEVEL: 252 + integer_data->common.security_level = int_value; 253 + break; 254 + case INT_LOWER_BOUND: 255 + integer_data->lower_bound = int_value; 256 + break; 257 + case INT_UPPER_BOUND: 258 + integer_data->upper_bound = int_value; 259 + break; 260 + case INT_SCALAR_INCREMENT: 261 + integer_data->scalar_increment = int_value; 262 + break; 263 + default: 264 + pr_warn("Invalid element: %d found in Integer attribute or data may be malformed\n", elem); 265 + break; 266 + } 267 + 268 + kfree(str_value); 269 + str_value = NULL; 270 + } 271 + exit_integer_package: 272 + kfree(str_value); 273 + return 0; 274 + } 275 + 276 + /** 277 + * hp_populate_integer_package_data() - 278 + * Populate all properties of an instance under integer attribute 279 + * 280 + * @integer_obj: ACPI object with integer data 281 + * @instance_id: The instance to enumerate 282 + * @attr_name_kobj: The parent kernel object 283 + */ 284 + int hp_populate_integer_package_data(union acpi_object *integer_obj, 285 + int instance_id, 286 + struct kobject *attr_name_kobj) 287 + { 288 + struct integer_data *integer_data = &bioscfg_drv.integer_data[instance_id]; 289 + 290 + integer_data->attr_name_kobj = attr_name_kobj; 291 + hp_populate_integer_elements_from_package(integer_obj, 292 + integer_obj->package.count, 293 + instance_id); 294 + hp_update_attribute_permissions(integer_data->common.is_readonly, 295 + &integer_current_val); 296 + hp_friendly_user_name_update(integer_data->common.path, 297 + attr_name_kobj->name, 298 + integer_data->common.display_name, 299 + sizeof(integer_data->common.display_name)); 300 + return sysfs_create_group(attr_name_kobj, &integer_attr_group); 301 + } 302 + 303 + static int hp_populate_integer_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size, 304 + int instance_id) 305 + { 306 + char *dst = NULL; 307 + int dst_size = *buffer_size / sizeof(u16); 308 + struct integer_data *integer_data = &bioscfg_drv.integer_data[instance_id]; 309 + int ret = 0; 310 + 311 + dst = kcalloc(dst_size, sizeof(char), GFP_KERNEL); 312 + if (!dst) 313 + return -ENOMEM; 314 + 315 + /* 316 + * Only data relevant to this driver and its functionality is 317 + * read. BIOS defines the order in which each * element is 318 + * read. Element 0 data is not relevant to this 319 + * driver hence it is ignored. For clarity, all element names 320 + * (DISPLAY_IN_UI) which defines the order in which is read 321 + * and the name matches the variable where the data is stored. 322 + * 323 + * In earlier implementation, reported errors were ignored 324 + * causing the data to remain uninitialized. It is not 325 + * possible to determine if data read from BIOS is valid or 326 + * not. It is for this reason functions may return a error 327 + * without validating the data itself. 328 + */ 329 + 330 + // VALUE: 331 + integer_data->current_value = 0; 332 + 333 + hp_get_string_from_buffer(&buffer_ptr, buffer_size, dst, dst_size); 334 + ret = kstrtoint(dst, 10, &integer_data->current_value); 335 + if (ret) 336 + pr_warn("Unable to convert string to integer: %s\n", dst); 337 + kfree(dst); 338 + 339 + // COMMON: 340 + ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, &integer_data->common); 341 + if (ret < 0) 342 + goto buffer_exit; 343 + 344 + // INT_LOWER_BOUND: 345 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 346 + &integer_data->lower_bound); 347 + if (ret < 0) 348 + goto buffer_exit; 349 + 350 + // INT_UPPER_BOUND: 351 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 352 + &integer_data->upper_bound); 353 + if (ret < 0) 354 + goto buffer_exit; 355 + 356 + // INT_SCALAR_INCREMENT: 357 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 358 + &integer_data->scalar_increment); 359 + 360 + buffer_exit: 361 + return ret; 362 + } 363 + 364 + /** 365 + * hp_populate_integer_buffer_data() - 366 + * Populate all properties of an instance under integer attribute 367 + * 368 + * @buffer_ptr: Buffer pointer 369 + * @buffer_size: Buffer size 370 + * @instance_id: The instance to enumerate 371 + * @attr_name_kobj: The parent kernel object 372 + */ 373 + int hp_populate_integer_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int instance_id, 374 + struct kobject *attr_name_kobj) 375 + { 376 + struct integer_data *integer_data = &bioscfg_drv.integer_data[instance_id]; 377 + int ret = 0; 378 + 379 + integer_data->attr_name_kobj = attr_name_kobj; 380 + 381 + /* Populate integer elements */ 382 + ret = hp_populate_integer_elements_from_buffer(buffer_ptr, buffer_size, 383 + instance_id); 384 + if (ret < 0) 385 + return ret; 386 + 387 + hp_update_attribute_permissions(integer_data->common.is_readonly, 388 + &integer_current_val); 389 + hp_friendly_user_name_update(integer_data->common.path, 390 + attr_name_kobj->name, 391 + integer_data->common.display_name, 392 + sizeof(integer_data->common.display_name)); 393 + 394 + return sysfs_create_group(attr_name_kobj, &integer_attr_group); 395 + } 396 + 397 + /** 398 + * hp_exit_integer_attributes() - Clear all attribute data 399 + * 400 + * Clears all data allocated for this group of attributes 401 + */ 402 + void hp_exit_integer_attributes(void) 403 + { 404 + int instance_id; 405 + 406 + for (instance_id = 0; instance_id < bioscfg_drv.integer_instances_count; 407 + instance_id++) { 408 + struct kobject *attr_name_kobj = 409 + bioscfg_drv.integer_data[instance_id].attr_name_kobj; 410 + 411 + if (attr_name_kobj) 412 + sysfs_remove_group(attr_name_kobj, &integer_attr_group); 413 + } 414 + bioscfg_drv.integer_instances_count = 0; 415 + 416 + kfree(bioscfg_drv.integer_data); 417 + bioscfg_drv.integer_data = NULL; 418 + }
+441
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to ordered list type attributes under 4 + * BIOS ORDERED LIST GUID for use with hp-bioscfg driver. 5 + * 6 + * Copyright (c) 2022 HP Development Company, L.P. 7 + */ 8 + 9 + #include "bioscfg.h" 10 + 11 + GET_INSTANCE_ID(ordered_list); 12 + 13 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 14 + { 15 + int instance_id = get_ordered_list_instance_id(kobj); 16 + 17 + if (instance_id < 0) 18 + return -EIO; 19 + 20 + return sysfs_emit(buf, "%s\n", 21 + bioscfg_drv.ordered_list_data[instance_id].current_value); 22 + } 23 + 24 + static int replace_char_str(u8 *buffer, char *repl_char, char *repl_with) 25 + { 26 + char *src = buffer; 27 + int buflen = strlen(buffer); 28 + int item; 29 + 30 + if (buflen < 1) 31 + return -EINVAL; 32 + 33 + for (item = 0; item < buflen; item++) 34 + if (src[item] == *repl_char) 35 + src[item] = *repl_with; 36 + 37 + return 0; 38 + } 39 + 40 + /** 41 + * validate_ordered_list_input() - 42 + * Validate input of current_value against possible values 43 + * 44 + * @instance: The instance on which input is validated 45 + * @buf: Input value 46 + */ 47 + static int validate_ordered_list_input(int instance, char *buf) 48 + { 49 + /* validation is done by BIOS. This validation function will 50 + * convert semicolon to commas. BIOS uses commas as 51 + * separators when reporting ordered-list values. 52 + */ 53 + return replace_char_str(buf, SEMICOLON_SEP, COMMA_SEP); 54 + } 55 + 56 + static void update_ordered_list_value(int instance, char *attr_value) 57 + { 58 + struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance]; 59 + 60 + strscpy(ordered_list_data->current_value, 61 + attr_value, 62 + sizeof(ordered_list_data->current_value)); 63 + } 64 + 65 + ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, ordered_list); 66 + static struct kobj_attribute ordered_list_display_name = 67 + __ATTR_RO(display_name); 68 + 69 + ATTRIBUTE_PROPERTY_STORE(current_value, ordered_list); 70 + static struct kobj_attribute ordered_list_current_val = 71 + __ATTR_RW_MODE(current_value, 0644); 72 + 73 + ATTRIBUTE_VALUES_PROPERTY_SHOW(elements, ordered_list, SEMICOLON_SEP); 74 + static struct kobj_attribute ordered_list_elements_val = 75 + __ATTR_RO(elements); 76 + 77 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 78 + char *buf) 79 + { 80 + return sysfs_emit(buf, "ordered-list\n"); 81 + } 82 + 83 + static struct kobj_attribute ordered_list_type = 84 + __ATTR_RO(type); 85 + 86 + static struct attribute *ordered_list_attrs[] = { 87 + &common_display_langcode.attr, 88 + &ordered_list_display_name.attr, 89 + &ordered_list_current_val.attr, 90 + &ordered_list_elements_val.attr, 91 + &ordered_list_type.attr, 92 + NULL 93 + }; 94 + 95 + static const struct attribute_group ordered_list_attr_group = { 96 + .attrs = ordered_list_attrs, 97 + }; 98 + 99 + int hp_alloc_ordered_list_data(void) 100 + { 101 + bioscfg_drv.ordered_list_instances_count = 102 + hp_get_instance_count(HP_WMI_BIOS_ORDERED_LIST_GUID); 103 + bioscfg_drv.ordered_list_data = kcalloc(bioscfg_drv.ordered_list_instances_count, 104 + sizeof(*bioscfg_drv.ordered_list_data), 105 + GFP_KERNEL); 106 + if (!bioscfg_drv.ordered_list_data) { 107 + bioscfg_drv.ordered_list_instances_count = 0; 108 + return -ENOMEM; 109 + } 110 + return 0; 111 + } 112 + 113 + /* Expected Values types associated with each element */ 114 + static const acpi_object_type expected_order_types[] = { 115 + [NAME] = ACPI_TYPE_STRING, 116 + [VALUE] = ACPI_TYPE_STRING, 117 + [PATH] = ACPI_TYPE_STRING, 118 + [IS_READONLY] = ACPI_TYPE_INTEGER, 119 + [DISPLAY_IN_UI] = ACPI_TYPE_INTEGER, 120 + [REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER, 121 + [SEQUENCE] = ACPI_TYPE_INTEGER, 122 + [PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER, 123 + [PREREQUISITES] = ACPI_TYPE_STRING, 124 + [SECURITY_LEVEL] = ACPI_TYPE_INTEGER, 125 + [ORD_LIST_SIZE] = ACPI_TYPE_INTEGER, 126 + [ORD_LIST_ELEMENTS] = ACPI_TYPE_STRING, 127 + }; 128 + 129 + static int hp_populate_ordered_list_elements_from_package(union acpi_object *order_obj, 130 + int order_obj_count, 131 + int instance_id) 132 + { 133 + char *str_value = NULL; 134 + int value_len = 0; 135 + int ret; 136 + u32 size; 137 + u32 int_value = 0; 138 + int elem; 139 + int olist_elem; 140 + int reqs; 141 + int eloc; 142 + char *tmpstr = NULL; 143 + char *part_tmp = NULL; 144 + int tmp_len = 0; 145 + char *part = NULL; 146 + struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id]; 147 + 148 + if (!order_obj) 149 + return -EINVAL; 150 + 151 + for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++) { 152 + 153 + switch (order_obj[elem].type) { 154 + case ACPI_TYPE_STRING: 155 + if (elem != PREREQUISITES && elem != ORD_LIST_ELEMENTS) { 156 + ret = hp_convert_hexstr_to_str(order_obj[elem].string.pointer, 157 + order_obj[elem].string.length, 158 + &str_value, &value_len); 159 + if (ret) 160 + continue; 161 + } 162 + break; 163 + case ACPI_TYPE_INTEGER: 164 + int_value = (u32)order_obj[elem].integer.value; 165 + break; 166 + default: 167 + pr_warn("Unsupported object type [%d]\n", order_obj[elem].type); 168 + continue; 169 + } 170 + 171 + /* Check that both expected and read object type match */ 172 + if (expected_order_types[eloc] != order_obj[elem].type) { 173 + pr_err("Error expected type %d for elem %d, but got type %d instead\n", 174 + expected_order_types[eloc], elem, order_obj[elem].type); 175 + kfree(str_value); 176 + return -EIO; 177 + } 178 + 179 + /* Assign appropriate element value to corresponding field*/ 180 + switch (eloc) { 181 + case VALUE: 182 + strscpy(ordered_list_data->current_value, 183 + str_value, sizeof(ordered_list_data->current_value)); 184 + replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP); 185 + break; 186 + case PATH: 187 + strscpy(ordered_list_data->common.path, str_value, 188 + sizeof(ordered_list_data->common.path)); 189 + break; 190 + case IS_READONLY: 191 + ordered_list_data->common.is_readonly = int_value; 192 + break; 193 + case DISPLAY_IN_UI: 194 + ordered_list_data->common.display_in_ui = int_value; 195 + break; 196 + case REQUIRES_PHYSICAL_PRESENCE: 197 + ordered_list_data->common.requires_physical_presence = int_value; 198 + break; 199 + case SEQUENCE: 200 + ordered_list_data->common.sequence = int_value; 201 + break; 202 + case PREREQUISITES_SIZE: 203 + if (int_value > MAX_PREREQUISITES_SIZE) { 204 + pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n"); 205 + int_value = MAX_PREREQUISITES_SIZE; 206 + } 207 + ordered_list_data->common.prerequisites_size = int_value; 208 + 209 + /* 210 + * This step is needed to keep the expected 211 + * element list pointing to the right obj[elem].type 212 + * when the size is zero. PREREQUISITES 213 + * object is omitted by BIOS when the size is 214 + * zero. 215 + */ 216 + if (int_value == 0) 217 + eloc++; 218 + break; 219 + case PREREQUISITES: 220 + size = min_t(u32, ordered_list_data->common.prerequisites_size, 221 + MAX_PREREQUISITES_SIZE); 222 + for (reqs = 0; reqs < size; reqs++) { 223 + ret = hp_convert_hexstr_to_str(order_obj[elem + reqs].string.pointer, 224 + order_obj[elem + reqs].string.length, 225 + &str_value, &value_len); 226 + 227 + if (ret) 228 + continue; 229 + 230 + strscpy(ordered_list_data->common.prerequisites[reqs], 231 + str_value, 232 + sizeof(ordered_list_data->common.prerequisites[reqs])); 233 + 234 + kfree(str_value); 235 + str_value = NULL; 236 + } 237 + break; 238 + 239 + case SECURITY_LEVEL: 240 + ordered_list_data->common.security_level = int_value; 241 + break; 242 + 243 + case ORD_LIST_SIZE: 244 + if (int_value > MAX_ELEMENTS_SIZE) { 245 + pr_warn("Order List size value exceeded the maximum number of elements supported or data may be malformed\n"); 246 + int_value = MAX_ELEMENTS_SIZE; 247 + } 248 + ordered_list_data->elements_size = int_value; 249 + 250 + /* 251 + * This step is needed to keep the expected 252 + * element list pointing to the right obj[elem].type 253 + * when the size is zero. ORD_LIST_ELEMENTS 254 + * object is omitted by BIOS when the size is 255 + * zero. 256 + */ 257 + if (int_value == 0) 258 + eloc++; 259 + break; 260 + case ORD_LIST_ELEMENTS: 261 + 262 + /* 263 + * Ordered list data is stored in hex and comma separated format 264 + * Convert the data and split it to show each element 265 + */ 266 + ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len); 267 + if (ret) 268 + goto exit_list; 269 + 270 + part_tmp = tmpstr; 271 + part = strsep(&part_tmp, COMMA_SEP); 272 + 273 + for (olist_elem = 0; olist_elem < MAX_ELEMENTS_SIZE && part; olist_elem++) { 274 + strscpy(ordered_list_data->elements[olist_elem], 275 + part, 276 + sizeof(ordered_list_data->elements[olist_elem])); 277 + part = strsep(&part_tmp, COMMA_SEP); 278 + } 279 + ordered_list_data->elements_size = olist_elem; 280 + 281 + kfree(str_value); 282 + str_value = NULL; 283 + break; 284 + default: 285 + pr_warn("Invalid element: %d found in Ordered_List attribute or data may be malformed\n", elem); 286 + break; 287 + } 288 + kfree(tmpstr); 289 + tmpstr = NULL; 290 + kfree(str_value); 291 + str_value = NULL; 292 + } 293 + 294 + exit_list: 295 + kfree(tmpstr); 296 + kfree(str_value); 297 + return 0; 298 + } 299 + 300 + /** 301 + * hp_populate_ordered_list_package_data() - 302 + * Populate all properties of an instance under ordered_list attribute 303 + * 304 + * @order_obj: ACPI object with ordered_list data 305 + * @instance_id: The instance to enumerate 306 + * @attr_name_kobj: The parent kernel object 307 + */ 308 + int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int instance_id, 309 + struct kobject *attr_name_kobj) 310 + { 311 + struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id]; 312 + 313 + ordered_list_data->attr_name_kobj = attr_name_kobj; 314 + 315 + hp_populate_ordered_list_elements_from_package(order_obj, 316 + order_obj->package.count, 317 + instance_id); 318 + hp_update_attribute_permissions(ordered_list_data->common.is_readonly, 319 + &ordered_list_current_val); 320 + hp_friendly_user_name_update(ordered_list_data->common.path, 321 + attr_name_kobj->name, 322 + ordered_list_data->common.display_name, 323 + sizeof(ordered_list_data->common.display_name)); 324 + return sysfs_create_group(attr_name_kobj, &ordered_list_attr_group); 325 + } 326 + 327 + static int hp_populate_ordered_list_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size, 328 + int instance_id) 329 + { 330 + int values; 331 + struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id]; 332 + int ret = 0; 333 + 334 + /* 335 + * Only data relevant to this driver and its functionality is 336 + * read. BIOS defines the order in which each * element is 337 + * read. Element 0 data is not relevant to this 338 + * driver hence it is ignored. For clarity, all element names 339 + * (DISPLAY_IN_UI) which defines the order in which is read 340 + * and the name matches the variable where the data is stored. 341 + * 342 + * In earlier implementation, reported errors were ignored 343 + * causing the data to remain uninitialized. It is not 344 + * possible to determine if data read from BIOS is valid or 345 + * not. It is for this reason functions may return a error 346 + * without validating the data itself. 347 + */ 348 + 349 + // VALUE: 350 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, ordered_list_data->current_value, 351 + sizeof(ordered_list_data->current_value)); 352 + if (ret < 0) 353 + goto buffer_exit; 354 + 355 + replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP); 356 + 357 + // COMMON: 358 + ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, 359 + &ordered_list_data->common); 360 + if (ret < 0) 361 + goto buffer_exit; 362 + 363 + // ORD_LIST_SIZE: 364 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 365 + &ordered_list_data->elements_size); 366 + 367 + if (ordered_list_data->elements_size > MAX_ELEMENTS_SIZE) { 368 + /* Report a message and limit elements size to maximum value */ 369 + pr_warn("Ordered List size value exceeded the maximum number of elements supported or data may be malformed\n"); 370 + ordered_list_data->elements_size = MAX_ELEMENTS_SIZE; 371 + } 372 + 373 + // ORD_LIST_ELEMENTS: 374 + for (values = 0; values < ordered_list_data->elements_size; values++) { 375 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, 376 + ordered_list_data->elements[values], 377 + sizeof(ordered_list_data->elements[values])); 378 + if (ret < 0) 379 + break; 380 + } 381 + 382 + buffer_exit: 383 + return ret; 384 + } 385 + 386 + /** 387 + * hp_populate_ordered_list_buffer_data() - Populate all properties of an 388 + * instance under ordered list attribute 389 + * 390 + * @buffer_ptr: Buffer pointer 391 + * @buffer_size: Buffer size 392 + * @instance_id: The instance to enumerate 393 + * @attr_name_kobj: The parent kernel object 394 + */ 395 + int hp_populate_ordered_list_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int instance_id, 396 + struct kobject *attr_name_kobj) 397 + { 398 + struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id]; 399 + int ret = 0; 400 + 401 + ordered_list_data->attr_name_kobj = attr_name_kobj; 402 + 403 + /* Populate ordered list elements */ 404 + ret = hp_populate_ordered_list_elements_from_buffer(buffer_ptr, buffer_size, 405 + instance_id); 406 + if (ret < 0) 407 + return ret; 408 + 409 + hp_update_attribute_permissions(ordered_list_data->common.is_readonly, 410 + &ordered_list_current_val); 411 + hp_friendly_user_name_update(ordered_list_data->common.path, 412 + attr_name_kobj->name, 413 + ordered_list_data->common.display_name, 414 + sizeof(ordered_list_data->common.display_name)); 415 + 416 + return sysfs_create_group(attr_name_kobj, &ordered_list_attr_group); 417 + } 418 + 419 + /** 420 + * hp_exit_ordered_list_attributes() - Clear all attribute data 421 + * 422 + * Clears all data allocated for this group of attributes 423 + */ 424 + void hp_exit_ordered_list_attributes(void) 425 + { 426 + int instance_id; 427 + 428 + for (instance_id = 0; instance_id < bioscfg_drv.ordered_list_instances_count; 429 + instance_id++) { 430 + struct kobject *attr_name_kobj = 431 + bioscfg_drv.ordered_list_data[instance_id].attr_name_kobj; 432 + 433 + if (attr_name_kobj) 434 + sysfs_remove_group(attr_name_kobj, 435 + &ordered_list_attr_group); 436 + } 437 + bioscfg_drv.ordered_list_instances_count = 0; 438 + 439 + kfree(bioscfg_drv.ordered_list_data); 440 + bioscfg_drv.ordered_list_data = NULL; 441 + }
+556
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to password object type attributes under 4 + * BIOS PASSWORD for use with hp-bioscfg driver. 5 + * 6 + * Copyright (c) 2022 HP Development Company, L.P. 7 + */ 8 + 9 + #include "bioscfg.h" 10 + #include <asm-generic/posix_types.h> 11 + 12 + GET_INSTANCE_ID(password); 13 + /* 14 + * Clear all passwords copied to memory for a particular 15 + * authentication instance 16 + */ 17 + static int clear_passwords(const int instance) 18 + { 19 + struct password_data *password_data = &bioscfg_drv.password_data[instance]; 20 + 21 + if (!password_data->is_enabled) 22 + return 0; 23 + 24 + memset(password_data->current_password, 25 + 0, sizeof(password_data->current_password)); 26 + memset(password_data->new_password, 27 + 0, sizeof(password_data->new_password)); 28 + 29 + return 0; 30 + } 31 + 32 + /* 33 + * Clear all credentials copied to memory for both Power-ON and Setup 34 + * BIOS instances 35 + */ 36 + int hp_clear_all_credentials(void) 37 + { 38 + int count = bioscfg_drv.password_instances_count; 39 + int instance; 40 + 41 + /* clear all passwords */ 42 + for (instance = 0; instance < count; instance++) 43 + clear_passwords(instance); 44 + 45 + /* clear auth_token */ 46 + kfree(bioscfg_drv.spm_data.auth_token); 47 + bioscfg_drv.spm_data.auth_token = NULL; 48 + 49 + return 0; 50 + } 51 + 52 + int hp_get_password_instance_for_type(const char *name) 53 + { 54 + int count = bioscfg_drv.password_instances_count; 55 + int instance; 56 + 57 + for (instance = 0; instance < count; instance++) 58 + if (!strcmp(bioscfg_drv.password_data[instance].common.display_name, name)) 59 + return instance; 60 + 61 + return -EINVAL; 62 + } 63 + 64 + static int validate_password_input(int instance_id, const char *buf) 65 + { 66 + int length; 67 + struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; 68 + 69 + length = strlen(buf); 70 + if (buf[length - 1] == '\n') 71 + length--; 72 + 73 + if (length > MAX_PASSWD_SIZE) 74 + return INVALID_BIOS_AUTH; 75 + 76 + if (password_data->min_password_length > length || 77 + password_data->max_password_length < length) 78 + return INVALID_BIOS_AUTH; 79 + return SUCCESS; 80 + } 81 + 82 + ATTRIBUTE_N_PROPERTY_SHOW(is_enabled, password); 83 + static struct kobj_attribute password_is_password_set = __ATTR_RO(is_enabled); 84 + 85 + static int store_password_instance(struct kobject *kobj, const char *buf, 86 + size_t count, bool is_current) 87 + { 88 + char *buf_cp; 89 + int id, ret = 0; 90 + 91 + buf_cp = kstrdup(buf, GFP_KERNEL); 92 + if (!buf_cp) 93 + return -ENOMEM; 94 + 95 + ret = hp_enforce_single_line_input(buf_cp, count); 96 + if (!ret) { 97 + id = get_password_instance_id(kobj); 98 + 99 + if (id >= 0) 100 + ret = validate_password_input(id, buf_cp); 101 + } 102 + 103 + if (!ret) { 104 + if (is_current) 105 + strscpy(bioscfg_drv.password_data[id].current_password, 106 + buf_cp, 107 + sizeof(bioscfg_drv.password_data[id].current_password)); 108 + else 109 + strscpy(bioscfg_drv.password_data[id].new_password, 110 + buf_cp, 111 + sizeof(bioscfg_drv.password_data[id].new_password)); 112 + } 113 + 114 + kfree(buf_cp); 115 + return ret < 0 ? ret : count; 116 + } 117 + 118 + static ssize_t current_password_store(struct kobject *kobj, 119 + struct kobj_attribute *attr, 120 + const char *buf, size_t count) 121 + { 122 + return store_password_instance(kobj, buf, count, true); 123 + } 124 + 125 + static struct kobj_attribute password_current_password = __ATTR_WO(current_password); 126 + 127 + static ssize_t new_password_store(struct kobject *kobj, 128 + struct kobj_attribute *attr, 129 + const char *buf, size_t count) 130 + { 131 + return store_password_instance(kobj, buf, count, true); 132 + } 133 + 134 + static struct kobj_attribute password_new_password = __ATTR_WO(new_password); 135 + 136 + ATTRIBUTE_N_PROPERTY_SHOW(min_password_length, password); 137 + static struct kobj_attribute password_min_password_length = __ATTR_RO(min_password_length); 138 + 139 + ATTRIBUTE_N_PROPERTY_SHOW(max_password_length, password); 140 + static struct kobj_attribute password_max_password_length = __ATTR_RO(max_password_length); 141 + 142 + static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 143 + { 144 + if (!strcmp(kobj->name, SETUP_PASSWD)) 145 + return sysfs_emit(buf, "%s\n", BIOS_ADMIN); 146 + 147 + if (!strcmp(kobj->name, POWER_ON_PASSWD)) 148 + return sysfs_emit(buf, "%s\n", POWER_ON); 149 + 150 + return -EIO; 151 + } 152 + 153 + static struct kobj_attribute password_role = __ATTR_RO(role); 154 + 155 + static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, 156 + char *buf) 157 + { 158 + int i = get_password_instance_id(kobj); 159 + 160 + if (i < 0) 161 + return i; 162 + 163 + if (bioscfg_drv.password_data[i].mechanism != PASSWORD) 164 + return -EINVAL; 165 + 166 + return sysfs_emit(buf, "%s\n", PASSWD_MECHANISM_TYPES); 167 + } 168 + 169 + static struct kobj_attribute password_mechanism = __ATTR_RO(mechanism); 170 + 171 + ATTRIBUTE_VALUES_PROPERTY_SHOW(encodings, password, SEMICOLON_SEP); 172 + static struct kobj_attribute password_encodings_val = __ATTR_RO(encodings); 173 + 174 + static struct attribute *password_attrs[] = { 175 + &password_is_password_set.attr, 176 + &password_min_password_length.attr, 177 + &password_max_password_length.attr, 178 + &password_current_password.attr, 179 + &password_new_password.attr, 180 + &password_role.attr, 181 + &password_mechanism.attr, 182 + &password_encodings_val.attr, 183 + NULL 184 + }; 185 + 186 + static const struct attribute_group password_attr_group = { 187 + .attrs = password_attrs 188 + }; 189 + 190 + int hp_alloc_password_data(void) 191 + { 192 + bioscfg_drv.password_instances_count = hp_get_instance_count(HP_WMI_BIOS_PASSWORD_GUID); 193 + bioscfg_drv.password_data = kcalloc(bioscfg_drv.password_instances_count, 194 + sizeof(*bioscfg_drv.password_data), GFP_KERNEL); 195 + if (!bioscfg_drv.password_data) { 196 + bioscfg_drv.password_instances_count = 0; 197 + return -ENOMEM; 198 + } 199 + 200 + return 0; 201 + } 202 + 203 + /* Expected Values types associated with each element */ 204 + static const acpi_object_type expected_password_types[] = { 205 + [NAME] = ACPI_TYPE_STRING, 206 + [VALUE] = ACPI_TYPE_STRING, 207 + [PATH] = ACPI_TYPE_STRING, 208 + [IS_READONLY] = ACPI_TYPE_INTEGER, 209 + [DISPLAY_IN_UI] = ACPI_TYPE_INTEGER, 210 + [REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER, 211 + [SEQUENCE] = ACPI_TYPE_INTEGER, 212 + [PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER, 213 + [PREREQUISITES] = ACPI_TYPE_STRING, 214 + [SECURITY_LEVEL] = ACPI_TYPE_INTEGER, 215 + [PSWD_MIN_LENGTH] = ACPI_TYPE_INTEGER, 216 + [PSWD_MAX_LENGTH] = ACPI_TYPE_INTEGER, 217 + [PSWD_SIZE] = ACPI_TYPE_INTEGER, 218 + [PSWD_ENCODINGS] = ACPI_TYPE_STRING, 219 + [PSWD_IS_SET] = ACPI_TYPE_INTEGER, 220 + }; 221 + 222 + static int hp_populate_password_elements_from_package(union acpi_object *password_obj, 223 + int password_obj_count, 224 + int instance_id) 225 + { 226 + char *str_value = NULL; 227 + int value_len; 228 + int ret; 229 + u32 size; 230 + u32 int_value = 0; 231 + int elem; 232 + int reqs; 233 + int eloc; 234 + int pos_values; 235 + struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; 236 + 237 + if (!password_obj) 238 + return -EINVAL; 239 + 240 + for (elem = 1, eloc = 1; elem < password_obj_count; elem++, eloc++) { 241 + /* ONLY look at the first PASSWORD_ELEM_CNT elements */ 242 + if (eloc == PSWD_ELEM_CNT) 243 + goto exit_package; 244 + 245 + switch (password_obj[elem].type) { 246 + case ACPI_TYPE_STRING: 247 + if (PREREQUISITES != elem && PSWD_ENCODINGS != elem) { 248 + ret = hp_convert_hexstr_to_str(password_obj[elem].string.pointer, 249 + password_obj[elem].string.length, 250 + &str_value, &value_len); 251 + if (ret) 252 + continue; 253 + } 254 + break; 255 + case ACPI_TYPE_INTEGER: 256 + int_value = (u32)password_obj[elem].integer.value; 257 + break; 258 + default: 259 + pr_warn("Unsupported object type [%d]\n", password_obj[elem].type); 260 + continue; 261 + } 262 + 263 + /* Check that both expected and read object type match */ 264 + if (expected_password_types[eloc] != password_obj[elem].type) { 265 + pr_err("Error expected type %d for elem %d, but got type %d instead\n", 266 + expected_password_types[eloc], elem, password_obj[elem].type); 267 + kfree(str_value); 268 + return -EIO; 269 + } 270 + 271 + /* Assign appropriate element value to corresponding field*/ 272 + switch (eloc) { 273 + case VALUE: 274 + break; 275 + case PATH: 276 + strscpy(password_data->common.path, str_value, 277 + sizeof(password_data->common.path)); 278 + break; 279 + case IS_READONLY: 280 + password_data->common.is_readonly = int_value; 281 + break; 282 + case DISPLAY_IN_UI: 283 + password_data->common.display_in_ui = int_value; 284 + break; 285 + case REQUIRES_PHYSICAL_PRESENCE: 286 + password_data->common.requires_physical_presence = int_value; 287 + break; 288 + case SEQUENCE: 289 + password_data->common.sequence = int_value; 290 + break; 291 + case PREREQUISITES_SIZE: 292 + if (int_value > MAX_PREREQUISITES_SIZE) { 293 + pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n"); 294 + int_value = MAX_PREREQUISITES_SIZE; 295 + } 296 + password_data->common.prerequisites_size = int_value; 297 + 298 + /* This step is needed to keep the expected 299 + * element list pointing to the right obj[elem].type 300 + * when the size is zero. PREREQUISITES 301 + * object is omitted by BIOS when the size is 302 + * zero. 303 + */ 304 + if (int_value == 0) 305 + eloc++; 306 + break; 307 + case PREREQUISITES: 308 + size = min_t(u32, password_data->common.prerequisites_size, 309 + MAX_PREREQUISITES_SIZE); 310 + 311 + for (reqs = 0; reqs < size; reqs++) { 312 + ret = hp_convert_hexstr_to_str(password_obj[elem + reqs].string.pointer, 313 + password_obj[elem + reqs].string.length, 314 + &str_value, &value_len); 315 + 316 + if (ret) 317 + break; 318 + 319 + strscpy(password_data->common.prerequisites[reqs], 320 + str_value, 321 + sizeof(password_data->common.prerequisites[reqs])); 322 + 323 + kfree(str_value); 324 + str_value = NULL; 325 + 326 + } 327 + break; 328 + case SECURITY_LEVEL: 329 + password_data->common.security_level = int_value; 330 + break; 331 + case PSWD_MIN_LENGTH: 332 + password_data->min_password_length = int_value; 333 + break; 334 + case PSWD_MAX_LENGTH: 335 + password_data->max_password_length = int_value; 336 + break; 337 + case PSWD_SIZE: 338 + 339 + if (int_value > MAX_ENCODINGS_SIZE) { 340 + pr_warn("Password Encoding size value exceeded the maximum number of elements supported or data may be malformed\n"); 341 + int_value = MAX_ENCODINGS_SIZE; 342 + } 343 + password_data->encodings_size = int_value; 344 + 345 + /* This step is needed to keep the expected 346 + * element list pointing to the right obj[elem].type 347 + * when the size is zero. PSWD_ENCODINGS 348 + * object is omitted by BIOS when the size is 349 + * zero. 350 + */ 351 + if (int_value == 0) 352 + eloc++; 353 + break; 354 + case PSWD_ENCODINGS: 355 + size = min_t(u32, password_data->encodings_size, MAX_ENCODINGS_SIZE); 356 + for (pos_values = 0; pos_values < size; pos_values++) { 357 + ret = hp_convert_hexstr_to_str(password_obj[elem + pos_values].string.pointer, 358 + password_obj[elem + pos_values].string.length, 359 + &str_value, &value_len); 360 + if (ret) 361 + break; 362 + 363 + strscpy(password_data->encodings[pos_values], 364 + str_value, 365 + sizeof(password_data->encodings[pos_values])); 366 + kfree(str_value); 367 + str_value = NULL; 368 + 369 + } 370 + break; 371 + case PSWD_IS_SET: 372 + password_data->is_enabled = int_value; 373 + break; 374 + default: 375 + pr_warn("Invalid element: %d found in Password attribute or data may be malformed\n", elem); 376 + break; 377 + } 378 + 379 + kfree(str_value); 380 + str_value = NULL; 381 + } 382 + 383 + exit_package: 384 + kfree(str_value); 385 + return 0; 386 + } 387 + 388 + /** 389 + * hp_populate_password_package_data() 390 + * Populate all properties for an instance under password attribute 391 + * 392 + * @password_obj: ACPI object with password data 393 + * @instance_id: The instance to enumerate 394 + * @attr_name_kobj: The parent kernel object 395 + */ 396 + int hp_populate_password_package_data(union acpi_object *password_obj, int instance_id, 397 + struct kobject *attr_name_kobj) 398 + { 399 + struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; 400 + 401 + password_data->attr_name_kobj = attr_name_kobj; 402 + 403 + hp_populate_password_elements_from_package(password_obj, 404 + password_obj->package.count, 405 + instance_id); 406 + 407 + hp_friendly_user_name_update(password_data->common.path, 408 + attr_name_kobj->name, 409 + password_data->common.display_name, 410 + sizeof(password_data->common.display_name)); 411 + 412 + if (!strcmp(attr_name_kobj->name, SETUP_PASSWD)) 413 + return sysfs_create_group(attr_name_kobj, &password_attr_group); 414 + 415 + return sysfs_create_group(attr_name_kobj, &password_attr_group); 416 + } 417 + 418 + static int hp_populate_password_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size, 419 + int instance_id) 420 + { 421 + int values; 422 + int isreadonly; 423 + struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; 424 + int ret = 0; 425 + 426 + /* 427 + * Only data relevant to this driver and its functionality is 428 + * read. BIOS defines the order in which each * element is 429 + * read. Element 0 data is not relevant to this 430 + * driver hence it is ignored. For clarity, all element names 431 + * (DISPLAY_IN_UI) which defines the order in which is read 432 + * and the name matches the variable where the data is stored. 433 + * 434 + * In earlier implementation, reported errors were ignored 435 + * causing the data to remain uninitialized. It is not 436 + * possible to determine if data read from BIOS is valid or 437 + * not. It is for this reason functions may return a error 438 + * without validating the data itself. 439 + */ 440 + 441 + // VALUE: 442 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, password_data->current_password, 443 + sizeof(password_data->current_password)); 444 + if (ret < 0) 445 + goto buffer_exit; 446 + 447 + // COMMON: 448 + ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, 449 + &password_data->common); 450 + if (ret < 0) 451 + goto buffer_exit; 452 + 453 + // PSWD_MIN_LENGTH: 454 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 455 + &password_data->min_password_length); 456 + if (ret < 0) 457 + goto buffer_exit; 458 + 459 + // PSWD_MAX_LENGTH: 460 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 461 + &password_data->max_password_length); 462 + if (ret < 0) 463 + goto buffer_exit; 464 + 465 + // PSWD_SIZE: 466 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 467 + &password_data->encodings_size); 468 + if (ret < 0) 469 + goto buffer_exit; 470 + 471 + if (password_data->encodings_size > MAX_ENCODINGS_SIZE) { 472 + /* Report a message and limit possible values size to maximum value */ 473 + pr_warn("Password Encoding size value exceeded the maximum number of elements supported or data may be malformed\n"); 474 + password_data->encodings_size = MAX_ENCODINGS_SIZE; 475 + } 476 + 477 + // PSWD_ENCODINGS: 478 + for (values = 0; values < password_data->encodings_size; values++) { 479 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, 480 + password_data->encodings[values], 481 + sizeof(password_data->encodings[values])); 482 + if (ret < 0) 483 + break; 484 + } 485 + 486 + // PSWD_IS_SET: 487 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, &isreadonly); 488 + if (ret < 0) 489 + goto buffer_exit; 490 + 491 + password_data->is_enabled = isreadonly ? true : false; 492 + 493 + buffer_exit: 494 + return ret; 495 + } 496 + 497 + /** 498 + * hp_populate_password_buffer_data() 499 + * Populate all properties for an instance under password object attribute 500 + * 501 + * @buffer_ptr: Buffer pointer 502 + * @buffer_size: Buffer size 503 + * @instance_id: The instance to enumerate 504 + * @attr_name_kobj: The parent kernel object 505 + */ 506 + int hp_populate_password_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int instance_id, 507 + struct kobject *attr_name_kobj) 508 + { 509 + struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; 510 + int ret = 0; 511 + 512 + password_data->attr_name_kobj = attr_name_kobj; 513 + 514 + /* Populate Password attributes */ 515 + ret = hp_populate_password_elements_from_buffer(buffer_ptr, buffer_size, 516 + instance_id); 517 + if (ret < 0) 518 + return ret; 519 + 520 + hp_friendly_user_name_update(password_data->common.path, 521 + attr_name_kobj->name, 522 + password_data->common.display_name, 523 + sizeof(password_data->common.display_name)); 524 + if (!strcmp(attr_name_kobj->name, SETUP_PASSWD)) 525 + return sysfs_create_group(attr_name_kobj, &password_attr_group); 526 + 527 + return sysfs_create_group(attr_name_kobj, &password_attr_group); 528 + } 529 + 530 + /** 531 + * hp_exit_password_attributes() - Clear all attribute data 532 + * 533 + * Clears all data allocated for this group of attributes 534 + */ 535 + void hp_exit_password_attributes(void) 536 + { 537 + int instance_id; 538 + 539 + for (instance_id = 0; instance_id < bioscfg_drv.password_instances_count; 540 + instance_id++) { 541 + struct kobject *attr_name_kobj = 542 + bioscfg_drv.password_data[instance_id].attr_name_kobj; 543 + 544 + if (attr_name_kobj) { 545 + if (!strcmp(attr_name_kobj->name, SETUP_PASSWD)) 546 + sysfs_remove_group(attr_name_kobj, 547 + &password_attr_group); 548 + else 549 + sysfs_remove_group(attr_name_kobj, 550 + &password_attr_group); 551 + } 552 + } 553 + bioscfg_drv.password_instances_count = 0; 554 + kfree(bioscfg_drv.password_data); 555 + bioscfg_drv.password_data = NULL; 556 + }
+381
drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to secure platform management object type 4 + * attributes under BIOS PASSWORD for use with hp-bioscfg driver 5 + * 6 + * Copyright (c) 2022 HP Development Company, L.P. 7 + */ 8 + 9 + #include "bioscfg.h" 10 + 11 + static const char * const spm_state_types[] = { 12 + "not provisioned", 13 + "provisioned", 14 + "provisioning in progress", 15 + }; 16 + 17 + static const char * const spm_mechanism_types[] = { 18 + "not provisioned", 19 + "signing-key", 20 + "endorsement-key", 21 + }; 22 + 23 + struct secureplatform_provisioning_data { 24 + u8 state; 25 + u8 version[2]; 26 + u8 reserved1; 27 + u32 features; 28 + u32 nonce; 29 + u8 reserved2[28]; 30 + u8 sk_mod[MAX_KEY_MOD_SIZE]; 31 + u8 kek_mod[MAX_KEY_MOD_SIZE]; 32 + }; 33 + 34 + /** 35 + * hp_calculate_security_buffer() - determines size of security buffer 36 + * for authentication scheme 37 + * 38 + * @authentication: the authentication content 39 + * 40 + * Currently only supported type is Admin password 41 + */ 42 + size_t hp_calculate_security_buffer(const char *authentication) 43 + { 44 + size_t size, authlen; 45 + 46 + if (!authentication) 47 + return sizeof(u16) * 2; 48 + 49 + authlen = strlen(authentication); 50 + if (!authlen) 51 + return sizeof(u16) * 2; 52 + 53 + size = sizeof(u16) + authlen * sizeof(u16); 54 + if (!strstarts(authentication, BEAM_PREFIX)) 55 + size += strlen(UTF_PREFIX) * sizeof(u16); 56 + 57 + return size; 58 + } 59 + 60 + /** 61 + * hp_populate_security_buffer() - builds a security buffer for 62 + * authentication scheme 63 + * 64 + * @authbuf: the security buffer 65 + * @authentication: the authentication content 66 + * 67 + * Currently only supported type is PLAIN TEXT 68 + */ 69 + int hp_populate_security_buffer(u16 *authbuf, const char *authentication) 70 + { 71 + u16 *auth = authbuf; 72 + char *strprefix = NULL; 73 + int ret = 0; 74 + 75 + if (strstarts(authentication, BEAM_PREFIX)) { 76 + /* 77 + * BEAM_PREFIX is append to authbuf when a signature 78 + * is provided and Sure Admin is enabled in BIOS 79 + */ 80 + /* BEAM_PREFIX found, convert part to unicode */ 81 + auth = hp_ascii_to_utf16_unicode(auth, authentication); 82 + if (!auth) 83 + return -EINVAL; 84 + 85 + } else { 86 + /* 87 + * UTF-16 prefix is append to the * authbuf when a BIOS 88 + * admin password is configured in BIOS 89 + */ 90 + 91 + /* append UTF_PREFIX to part and then convert it to unicode */ 92 + strprefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX, 93 + authentication); 94 + if (!strprefix) 95 + return -ENOMEM; 96 + 97 + auth = hp_ascii_to_utf16_unicode(auth, strprefix); 98 + kfree(strprefix); 99 + 100 + if (!auth) { 101 + ret = -EINVAL; 102 + goto out_buffer; 103 + } 104 + } 105 + 106 + out_buffer: 107 + return ret; 108 + } 109 + 110 + static ssize_t update_spm_state(void) 111 + { 112 + struct secureplatform_provisioning_data data; 113 + int ret; 114 + 115 + ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_GET_STATE, 116 + HPWMI_SECUREPLATFORM, &data, 0, 117 + sizeof(data)); 118 + if (ret < 0) 119 + return ret; 120 + 121 + bioscfg_drv.spm_data.mechanism = data.state; 122 + if (bioscfg_drv.spm_data.mechanism) 123 + bioscfg_drv.spm_data.is_enabled = 1; 124 + 125 + return 0; 126 + } 127 + 128 + static ssize_t statusbin(struct kobject *kobj, 129 + struct kobj_attribute *attr, 130 + struct secureplatform_provisioning_data *buf) 131 + { 132 + int ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_GET_STATE, 133 + HPWMI_SECUREPLATFORM, buf, 0, 134 + sizeof(*buf)); 135 + 136 + if (ret < 0) 137 + return ret; 138 + 139 + return sizeof(struct secureplatform_provisioning_data); 140 + } 141 + 142 + /* 143 + * status_show - Reads SPM status 144 + */ 145 + static ssize_t status_show(struct kobject *kobj, struct kobj_attribute 146 + *attr, char *buf) 147 + { 148 + int ret, i; 149 + int len = 0; 150 + struct secureplatform_provisioning_data data; 151 + 152 + ret = statusbin(kobj, attr, &data); 153 + if (ret < 0) 154 + return ret; 155 + 156 + /* 157 + * 'status' is a read-only file that returns ASCII text in 158 + * JSON format reporting the status information. 159 + * 160 + * "State": "not provisioned | provisioned | provisioning in progress ", 161 + * "Version": " Major. Minor ", 162 + * "Nonce": <16-bit unsigned number display in base 10>, 163 + * "FeaturesInUse": <16-bit unsigned number display in base 10>, 164 + * "EndorsementKeyMod": "<256 bytes in base64>", 165 + * "SigningKeyMod": "<256 bytes in base64>" 166 + */ 167 + 168 + len += sysfs_emit_at(buf, len, "{\n"); 169 + len += sysfs_emit_at(buf, len, "\t\"State\": \"%s\",\n", 170 + spm_state_types[data.state]); 171 + len += sysfs_emit_at(buf, len, "\t\"Version\": \"%d.%d\"", 172 + data.version[0], data.version[1]); 173 + 174 + /* 175 + * state == 0 means secure platform management 176 + * feature is not configured in BIOS. 177 + */ 178 + if (data.state == 0) { 179 + len += sysfs_emit_at(buf, len, "\n"); 180 + goto status_exit; 181 + } else { 182 + len += sysfs_emit_at(buf, len, ",\n"); 183 + } 184 + 185 + len += sysfs_emit_at(buf, len, "\t\"Nonce\": %d,\n", data.nonce); 186 + len += sysfs_emit_at(buf, len, "\t\"FeaturesInUse\": %d,\n", data.features); 187 + len += sysfs_emit_at(buf, len, "\t\"EndorsementKeyMod\": \""); 188 + 189 + for (i = 255; i >= 0; i--) 190 + len += sysfs_emit_at(buf, len, " %u", data.kek_mod[i]); 191 + 192 + len += sysfs_emit_at(buf, len, " \",\n"); 193 + len += sysfs_emit_at(buf, len, "\t\"SigningKeyMod\": \""); 194 + 195 + for (i = 255; i >= 0; i--) 196 + len += sysfs_emit_at(buf, len, " %u", data.sk_mod[i]); 197 + 198 + /* Return buf contents */ 199 + len += sysfs_emit_at(buf, len, " \"\n"); 200 + 201 + status_exit: 202 + len += sysfs_emit_at(buf, len, "}\n"); 203 + 204 + return len; 205 + } 206 + 207 + static struct kobj_attribute password_spm_status = __ATTR_RO(status); 208 + 209 + ATTRIBUTE_SPM_N_PROPERTY_SHOW(is_enabled, spm); 210 + static struct kobj_attribute password_spm_is_key_enabled = __ATTR_RO(is_enabled); 211 + 212 + static ssize_t key_mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, 213 + char *buf) 214 + { 215 + return sysfs_emit(buf, "%s\n", 216 + spm_mechanism_types[bioscfg_drv.spm_data.mechanism]); 217 + } 218 + 219 + static struct kobj_attribute password_spm_key_mechanism = __ATTR_RO(key_mechanism); 220 + 221 + static ssize_t sk_store(struct kobject *kobj, 222 + struct kobj_attribute *attr, 223 + const char *buf, size_t count) 224 + { 225 + int ret; 226 + int length; 227 + 228 + length = count; 229 + if (buf[length - 1] == '\n') 230 + length--; 231 + 232 + /* allocate space and copy current signing key */ 233 + bioscfg_drv.spm_data.signing_key = kmemdup(buf, length, GFP_KERNEL); 234 + if (!bioscfg_drv.spm_data.signing_key) 235 + return -ENOMEM; 236 + 237 + /* submit signing key payload */ 238 + ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK, 239 + HPWMI_SECUREPLATFORM, 240 + (void *)bioscfg_drv.spm_data.signing_key, 241 + count, 0); 242 + 243 + if (!ret) { 244 + bioscfg_drv.spm_data.mechanism = SIGNING_KEY; 245 + hp_set_reboot_and_signal_event(); 246 + } 247 + 248 + kfree(bioscfg_drv.spm_data.signing_key); 249 + bioscfg_drv.spm_data.signing_key = NULL; 250 + 251 + return ret ? ret : count; 252 + } 253 + 254 + static struct kobj_attribute password_spm_signing_key = __ATTR_WO(sk); 255 + 256 + static ssize_t kek_store(struct kobject *kobj, 257 + struct kobj_attribute *attr, 258 + const char *buf, size_t count) 259 + { 260 + int ret; 261 + int length; 262 + 263 + length = count; 264 + if (buf[length - 1] == '\n') 265 + length--; 266 + 267 + /* allocate space and copy current signing key */ 268 + bioscfg_drv.spm_data.endorsement_key = kmemdup(buf, length, GFP_KERNEL); 269 + if (!bioscfg_drv.spm_data.endorsement_key) { 270 + ret = -ENOMEM; 271 + goto exit_kek; 272 + } 273 + 274 + ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_KEK, 275 + HPWMI_SECUREPLATFORM, 276 + (void *)bioscfg_drv.spm_data.endorsement_key, 277 + count, 0); 278 + 279 + if (!ret) { 280 + bioscfg_drv.spm_data.mechanism = ENDORSEMENT_KEY; 281 + hp_set_reboot_and_signal_event(); 282 + } 283 + 284 + exit_kek: 285 + kfree(bioscfg_drv.spm_data.endorsement_key); 286 + bioscfg_drv.spm_data.endorsement_key = NULL; 287 + 288 + return ret ? ret : count; 289 + } 290 + 291 + static struct kobj_attribute password_spm_endorsement_key = __ATTR_WO(kek); 292 + 293 + static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, 294 + char *buf) 295 + { 296 + return sysfs_emit(buf, "%s\n", BIOS_SPM); 297 + } 298 + 299 + static struct kobj_attribute password_spm_role = __ATTR_RO(role); 300 + 301 + static ssize_t auth_token_store(struct kobject *kobj, 302 + struct kobj_attribute *attr, 303 + const char *buf, size_t count) 304 + { 305 + int ret = 0; 306 + int length; 307 + 308 + length = count; 309 + if (buf[length - 1] == '\n') 310 + length--; 311 + 312 + /* allocate space and copy current auth token */ 313 + bioscfg_drv.spm_data.auth_token = kmemdup(buf, length, GFP_KERNEL); 314 + if (!bioscfg_drv.spm_data.auth_token) { 315 + ret = -ENOMEM; 316 + goto exit_token; 317 + } 318 + 319 + return count; 320 + 321 + exit_token: 322 + kfree(bioscfg_drv.spm_data.auth_token); 323 + bioscfg_drv.spm_data.auth_token = NULL; 324 + 325 + return ret; 326 + } 327 + 328 + static struct kobj_attribute password_spm_auth_token = __ATTR_WO(auth_token); 329 + 330 + static struct attribute *secure_platform_attrs[] = { 331 + &password_spm_is_key_enabled.attr, 332 + &password_spm_signing_key.attr, 333 + &password_spm_endorsement_key.attr, 334 + &password_spm_key_mechanism.attr, 335 + &password_spm_status.attr, 336 + &password_spm_role.attr, 337 + &password_spm_auth_token.attr, 338 + NULL, 339 + }; 340 + 341 + static const struct attribute_group secure_platform_attr_group = { 342 + .attrs = secure_platform_attrs, 343 + }; 344 + 345 + void hp_exit_secure_platform_attributes(void) 346 + { 347 + /* remove secure platform sysfs entry and free key data*/ 348 + 349 + kfree(bioscfg_drv.spm_data.endorsement_key); 350 + bioscfg_drv.spm_data.endorsement_key = NULL; 351 + 352 + kfree(bioscfg_drv.spm_data.signing_key); 353 + bioscfg_drv.spm_data.signing_key = NULL; 354 + 355 + kfree(bioscfg_drv.spm_data.auth_token); 356 + bioscfg_drv.spm_data.auth_token = NULL; 357 + 358 + if (bioscfg_drv.spm_data.attr_name_kobj) 359 + sysfs_remove_group(bioscfg_drv.spm_data.attr_name_kobj, 360 + &secure_platform_attr_group); 361 + } 362 + 363 + int hp_populate_secure_platform_data(struct kobject *attr_name_kobj) 364 + { 365 + /* Populate data for Secure Platform Management */ 366 + bioscfg_drv.spm_data.attr_name_kobj = attr_name_kobj; 367 + 368 + strscpy(bioscfg_drv.spm_data.attribute_name, SPM_STR, 369 + sizeof(bioscfg_drv.spm_data.attribute_name)); 370 + 371 + bioscfg_drv.spm_data.is_enabled = 0; 372 + bioscfg_drv.spm_data.mechanism = 0; 373 + bioscfg_drv.pending_reboot = false; 374 + update_spm_state(); 375 + 376 + bioscfg_drv.spm_data.endorsement_key = NULL; 377 + bioscfg_drv.spm_data.signing_key = NULL; 378 + bioscfg_drv.spm_data.auth_token = NULL; 379 + 380 + return sysfs_create_group(attr_name_kobj, &secure_platform_attr_group); 381 + }
+395
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to string type attributes under 4 + * HP_WMI_BIOS_STRING_GUID for use with hp-bioscfg driver. 5 + * 6 + * Copyright (c) 2022 HP Development Company, L.P. 7 + */ 8 + 9 + #include "bioscfg.h" 10 + 11 + #define WMI_STRING_TYPE "HPBIOS_BIOSString" 12 + 13 + GET_INSTANCE_ID(string); 14 + 15 + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 16 + { 17 + int instance_id = get_string_instance_id(kobj); 18 + 19 + if (instance_id < 0) 20 + return -EIO; 21 + 22 + return sysfs_emit(buf, "%s\n", 23 + bioscfg_drv.string_data[instance_id].current_value); 24 + } 25 + 26 + /** 27 + * validate_string_input() - 28 + * Validate input of current_value against min and max lengths 29 + * 30 + * @instance_id: The instance on which input is validated 31 + * @buf: Input value 32 + */ 33 + static int validate_string_input(int instance_id, const char *buf) 34 + { 35 + int in_len = strlen(buf); 36 + struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; 37 + 38 + /* BIOS treats it as a read only attribute */ 39 + if (string_data->common.is_readonly) 40 + return -EIO; 41 + 42 + if (in_len < string_data->min_length || in_len > string_data->max_length) 43 + return -ERANGE; 44 + 45 + return 0; 46 + } 47 + 48 + static void update_string_value(int instance_id, char *attr_value) 49 + { 50 + struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; 51 + 52 + /* Write settings to BIOS */ 53 + strscpy(string_data->current_value, attr_value, sizeof(string_data->current_value)); 54 + } 55 + 56 + /* 57 + * ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name_language_code, string); 58 + * static struct kobj_attribute string_display_langcode = 59 + * __ATTR_RO(display_name_language_code); 60 + */ 61 + 62 + ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, string); 63 + static struct kobj_attribute string_display_name = 64 + __ATTR_RO(display_name); 65 + 66 + ATTRIBUTE_PROPERTY_STORE(current_value, string); 67 + static struct kobj_attribute string_current_val = 68 + __ATTR_RW_MODE(current_value, 0644); 69 + 70 + ATTRIBUTE_N_PROPERTY_SHOW(min_length, string); 71 + static struct kobj_attribute string_min_length = 72 + __ATTR_RO(min_length); 73 + 74 + ATTRIBUTE_N_PROPERTY_SHOW(max_length, string); 75 + static struct kobj_attribute string_max_length = 76 + __ATTR_RO(max_length); 77 + 78 + static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, 79 + char *buf) 80 + { 81 + return sysfs_emit(buf, "string\n"); 82 + } 83 + 84 + static struct kobj_attribute string_type = 85 + __ATTR_RO(type); 86 + 87 + static struct attribute *string_attrs[] = { 88 + &common_display_langcode.attr, 89 + &string_display_name.attr, 90 + &string_current_val.attr, 91 + &string_min_length.attr, 92 + &string_max_length.attr, 93 + &string_type.attr, 94 + NULL 95 + }; 96 + 97 + static const struct attribute_group string_attr_group = { 98 + .attrs = string_attrs, 99 + }; 100 + 101 + int hp_alloc_string_data(void) 102 + { 103 + bioscfg_drv.string_instances_count = hp_get_instance_count(HP_WMI_BIOS_STRING_GUID); 104 + bioscfg_drv.string_data = kcalloc(bioscfg_drv.string_instances_count, 105 + sizeof(*bioscfg_drv.string_data), GFP_KERNEL); 106 + if (!bioscfg_drv.string_data) { 107 + bioscfg_drv.string_instances_count = 0; 108 + return -ENOMEM; 109 + } 110 + return 0; 111 + } 112 + 113 + /* Expected Values types associated with each element */ 114 + static const acpi_object_type expected_string_types[] = { 115 + [NAME] = ACPI_TYPE_STRING, 116 + [VALUE] = ACPI_TYPE_STRING, 117 + [PATH] = ACPI_TYPE_STRING, 118 + [IS_READONLY] = ACPI_TYPE_INTEGER, 119 + [DISPLAY_IN_UI] = ACPI_TYPE_INTEGER, 120 + [REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER, 121 + [SEQUENCE] = ACPI_TYPE_INTEGER, 122 + [PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER, 123 + [PREREQUISITES] = ACPI_TYPE_STRING, 124 + [SECURITY_LEVEL] = ACPI_TYPE_INTEGER, 125 + [STR_MIN_LENGTH] = ACPI_TYPE_INTEGER, 126 + [STR_MAX_LENGTH] = ACPI_TYPE_INTEGER, 127 + }; 128 + 129 + static int hp_populate_string_elements_from_package(union acpi_object *string_obj, 130 + int string_obj_count, 131 + int instance_id) 132 + { 133 + char *str_value = NULL; 134 + int value_len; 135 + int ret = 0; 136 + u32 int_value = 0; 137 + int elem; 138 + int reqs; 139 + int eloc; 140 + int size; 141 + struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; 142 + 143 + if (!string_obj) 144 + return -EINVAL; 145 + 146 + for (elem = 1, eloc = 1; elem < string_obj_count; elem++, eloc++) { 147 + /* ONLY look at the first STRING_ELEM_CNT elements */ 148 + if (eloc == STR_ELEM_CNT) 149 + goto exit_string_package; 150 + 151 + switch (string_obj[elem].type) { 152 + case ACPI_TYPE_STRING: 153 + if (elem != PREREQUISITES) { 154 + ret = hp_convert_hexstr_to_str(string_obj[elem].string.pointer, 155 + string_obj[elem].string.length, 156 + &str_value, &value_len); 157 + 158 + if (ret) 159 + continue; 160 + } 161 + break; 162 + case ACPI_TYPE_INTEGER: 163 + int_value = (u32)string_obj[elem].integer.value; 164 + break; 165 + default: 166 + pr_warn("Unsupported object type [%d]\n", string_obj[elem].type); 167 + continue; 168 + } 169 + 170 + /* Check that both expected and read object type match */ 171 + if (expected_string_types[eloc] != string_obj[elem].type) { 172 + pr_err("Error expected type %d for elem %d, but got type %d instead\n", 173 + expected_string_types[eloc], elem, string_obj[elem].type); 174 + kfree(str_value); 175 + return -EIO; 176 + } 177 + 178 + /* Assign appropriate element value to corresponding field*/ 179 + switch (eloc) { 180 + case VALUE: 181 + strscpy(string_data->current_value, 182 + str_value, sizeof(string_data->current_value)); 183 + break; 184 + case PATH: 185 + strscpy(string_data->common.path, str_value, 186 + sizeof(string_data->common.path)); 187 + break; 188 + case IS_READONLY: 189 + string_data->common.is_readonly = int_value; 190 + break; 191 + case DISPLAY_IN_UI: 192 + string_data->common.display_in_ui = int_value; 193 + break; 194 + case REQUIRES_PHYSICAL_PRESENCE: 195 + string_data->common.requires_physical_presence = int_value; 196 + break; 197 + case SEQUENCE: 198 + string_data->common.sequence = int_value; 199 + break; 200 + case PREREQUISITES_SIZE: 201 + if (int_value > MAX_PREREQUISITES_SIZE) { 202 + pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n"); 203 + int_value = MAX_PREREQUISITES_SIZE; 204 + } 205 + string_data->common.prerequisites_size = int_value; 206 + 207 + /* 208 + * This step is needed to keep the expected 209 + * element list pointing to the right obj[elem].type 210 + * when the size is zero. PREREQUISITES 211 + * object is omitted by BIOS when the size is 212 + * zero. 213 + */ 214 + if (string_data->common.prerequisites_size == 0) 215 + eloc++; 216 + break; 217 + case PREREQUISITES: 218 + size = min_t(u32, string_data->common.prerequisites_size, 219 + MAX_PREREQUISITES_SIZE); 220 + 221 + for (reqs = 0; reqs < size; reqs++) { 222 + if (elem >= string_obj_count) { 223 + pr_err("Error elem-objects package is too small\n"); 224 + return -EINVAL; 225 + } 226 + 227 + ret = hp_convert_hexstr_to_str(string_obj[elem + reqs].string.pointer, 228 + string_obj[elem + reqs].string.length, 229 + &str_value, &value_len); 230 + 231 + if (ret) 232 + continue; 233 + 234 + strscpy(string_data->common.prerequisites[reqs], 235 + str_value, 236 + sizeof(string_data->common.prerequisites[reqs])); 237 + kfree(str_value); 238 + str_value = NULL; 239 + } 240 + break; 241 + 242 + case SECURITY_LEVEL: 243 + string_data->common.security_level = int_value; 244 + break; 245 + case STR_MIN_LENGTH: 246 + string_data->min_length = int_value; 247 + break; 248 + case STR_MAX_LENGTH: 249 + string_data->max_length = int_value; 250 + break; 251 + default: 252 + pr_warn("Invalid element: %d found in String attribute or data may be malformed\n", elem); 253 + break; 254 + } 255 + 256 + kfree(str_value); 257 + str_value = NULL; 258 + } 259 + 260 + exit_string_package: 261 + kfree(str_value); 262 + return 0; 263 + } 264 + 265 + /** 266 + * hp_populate_string_package_data() - 267 + * Populate all properties of an instance under string attribute 268 + * 269 + * @string_obj: ACPI object with string data 270 + * @instance_id: The instance to enumerate 271 + * @attr_name_kobj: The parent kernel object 272 + */ 273 + int hp_populate_string_package_data(union acpi_object *string_obj, 274 + int instance_id, 275 + struct kobject *attr_name_kobj) 276 + { 277 + struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; 278 + 279 + string_data->attr_name_kobj = attr_name_kobj; 280 + 281 + hp_populate_string_elements_from_package(string_obj, 282 + string_obj->package.count, 283 + instance_id); 284 + 285 + hp_update_attribute_permissions(string_data->common.is_readonly, 286 + &string_current_val); 287 + hp_friendly_user_name_update(string_data->common.path, 288 + attr_name_kobj->name, 289 + string_data->common.display_name, 290 + sizeof(string_data->common.display_name)); 291 + return sysfs_create_group(attr_name_kobj, &string_attr_group); 292 + } 293 + 294 + static int hp_populate_string_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size, 295 + int instance_id) 296 + { 297 + int ret = 0; 298 + struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; 299 + 300 + /* 301 + * Only data relevant to this driver and its functionality is 302 + * read. BIOS defines the order in which each * element is 303 + * read. Element 0 data is not relevant to this 304 + * driver hence it is ignored. For clarity, all element names 305 + * (DISPLAY_IN_UI) which defines the order in which is read 306 + * and the name matches the variable where the data is stored. 307 + * 308 + * In earlier implementation, reported errors were ignored 309 + * causing the data to remain uninitialized. It is not 310 + * possible to determine if data read from BIOS is valid or 311 + * not. It is for this reason functions may return a error 312 + * without validating the data itself. 313 + */ 314 + 315 + // VALUE: 316 + ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, string_data->current_value, 317 + sizeof(string_data->current_value)); 318 + if (ret < 0) 319 + goto buffer_exit; 320 + 321 + // COMMON: 322 + ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, &string_data->common); 323 + if (ret < 0) 324 + goto buffer_exit; 325 + 326 + // STR_MIN_LENGTH: 327 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 328 + &string_data->min_length); 329 + if (ret < 0) 330 + goto buffer_exit; 331 + 332 + // STR_MAX_LENGTH: 333 + ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size, 334 + &string_data->max_length); 335 + 336 + buffer_exit: 337 + 338 + return ret; 339 + } 340 + 341 + /** 342 + * hp_populate_string_buffer_data() - 343 + * Populate all properties of an instance under string attribute 344 + * 345 + * @buffer_ptr: Buffer pointer 346 + * @buffer_size: Buffer size 347 + * @instance_id: The instance to enumerate 348 + * @attr_name_kobj: The parent kernel object 349 + */ 350 + int hp_populate_string_buffer_data(u8 *buffer_ptr, u32 *buffer_size, 351 + int instance_id, 352 + struct kobject *attr_name_kobj) 353 + { 354 + struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; 355 + int ret = 0; 356 + 357 + string_data->attr_name_kobj = attr_name_kobj; 358 + 359 + ret = hp_populate_string_elements_from_buffer(buffer_ptr, buffer_size, 360 + instance_id); 361 + if (ret < 0) 362 + return ret; 363 + 364 + hp_update_attribute_permissions(string_data->common.is_readonly, 365 + &string_current_val); 366 + hp_friendly_user_name_update(string_data->common.path, 367 + attr_name_kobj->name, 368 + string_data->common.display_name, 369 + sizeof(string_data->common.display_name)); 370 + 371 + return sysfs_create_group(attr_name_kobj, &string_attr_group); 372 + } 373 + 374 + /** 375 + * hp_exit_string_attributes() - Clear all attribute data 376 + * 377 + * Clears all data allocated for this group of attributes 378 + */ 379 + void hp_exit_string_attributes(void) 380 + { 381 + int instance_id; 382 + 383 + for (instance_id = 0; instance_id < bioscfg_drv.string_instances_count; 384 + instance_id++) { 385 + struct kobject *attr_name_kobj = 386 + bioscfg_drv.string_data[instance_id].attr_name_kobj; 387 + 388 + if (attr_name_kobj) 389 + sysfs_remove_group(attr_name_kobj, &string_attr_group); 390 + } 391 + bioscfg_drv.string_instances_count = 0; 392 + 393 + kfree(bioscfg_drv.string_data); 394 + bioscfg_drv.string_data = NULL; 395 + }
+132
drivers/platform/x86/hp/hp-bioscfg/surestart-attributes.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Functions corresponding to sure start object type attributes under 4 + * BIOS for use with hp-bioscfg driver 5 + * 6 + * Copyright (c) 2022 HP Development Company, L.P. 7 + */ 8 + 9 + #include "bioscfg.h" 10 + #include <linux/types.h> 11 + 12 + /* Maximum number of log entries supported when log entry size is 16 13 + * bytes. This value is calculated by dividing 4096 (page size) by 14 + * log entry size. 15 + */ 16 + #define LOG_MAX_ENTRIES 254 17 + 18 + /* 19 + * Current Log entry size. This value size will change in the 20 + * future. The driver reads a total of 128 bytes for each log entry 21 + * provided by BIOS but only the first 16 bytes are used/read. 22 + */ 23 + #define LOG_ENTRY_SIZE 16 24 + 25 + /* 26 + * audit_log_entry_count_show - Reports the number of 27 + * existing audit log entries available 28 + * to be read 29 + */ 30 + static ssize_t audit_log_entry_count_show(struct kobject *kobj, 31 + struct kobj_attribute *attr, char *buf) 32 + { 33 + int ret; 34 + u32 count = 0; 35 + 36 + ret = hp_wmi_perform_query(HPWMI_SURESTART_GET_LOG_COUNT, 37 + HPWMI_SURESTART, 38 + &count, 1, sizeof(count)); 39 + 40 + if (ret < 0) 41 + return ret; 42 + 43 + return sysfs_emit(buf, "%d,%d,%d\n", count, LOG_ENTRY_SIZE, 44 + LOG_MAX_ENTRIES); 45 + } 46 + 47 + /* 48 + * audit_log_entries_show() - Return all entries found in log file 49 + */ 50 + static ssize_t audit_log_entries_show(struct kobject *kobj, 51 + struct kobj_attribute *attr, char *buf) 52 + { 53 + int ret; 54 + int i; 55 + u32 count = 0; 56 + u8 audit_log_buffer[128]; 57 + 58 + // Get the number of event logs 59 + ret = hp_wmi_perform_query(HPWMI_SURESTART_GET_LOG_COUNT, 60 + HPWMI_SURESTART, 61 + &count, 1, sizeof(count)); 62 + 63 + if (ret < 0) 64 + return ret; 65 + 66 + /* 67 + * The show() api will not work if the audit logs ever go 68 + * beyond 4KB 69 + */ 70 + if (count * LOG_ENTRY_SIZE > PAGE_SIZE) 71 + return -EIO; 72 + 73 + /* 74 + * We are guaranteed the buffer is 4KB so today all the event 75 + * logs will fit 76 + */ 77 + for (i = 0; i < count; i++) { 78 + audit_log_buffer[0] = i + 1; 79 + 80 + /* 81 + * read audit log entry at a time. 'buf' input value 82 + * provides the audit log entry to be read. On 83 + * input, Byte 0 = Audit Log entry number from 84 + * beginning (1..254) 85 + * Entry number 1 is the newest entry whereas the 86 + * highest entry number (number of entries) is the 87 + * oldest entry. 88 + */ 89 + ret = hp_wmi_perform_query(HPWMI_SURESTART_GET_LOG, 90 + HPWMI_SURESTART, 91 + audit_log_buffer, 1, 128); 92 + 93 + if (ret < 0 || (LOG_ENTRY_SIZE * i) > PAGE_SIZE) { 94 + /* 95 + * Encountered a failure while reading 96 + * individual logs. Only a partial list of 97 + * audit log will be returned. 98 + */ 99 + break; 100 + } else { 101 + memcpy(buf, audit_log_buffer, LOG_ENTRY_SIZE); 102 + buf += LOG_ENTRY_SIZE; 103 + } 104 + } 105 + 106 + return i * LOG_ENTRY_SIZE; 107 + } 108 + 109 + static struct kobj_attribute sure_start_audit_log_entry_count = __ATTR_RO(audit_log_entry_count); 110 + static struct kobj_attribute sure_start_audit_log_entries = __ATTR_RO(audit_log_entries); 111 + 112 + static struct attribute *sure_start_attrs[] = { 113 + &sure_start_audit_log_entry_count.attr, 114 + &sure_start_audit_log_entries.attr, 115 + NULL 116 + }; 117 + 118 + static const struct attribute_group sure_start_attr_group = { 119 + .attrs = sure_start_attrs, 120 + }; 121 + 122 + void hp_exit_sure_start_attributes(void) 123 + { 124 + sysfs_remove_group(bioscfg_drv.sure_start_attr_kobj, 125 + &sure_start_attr_group); 126 + } 127 + 128 + int hp_populate_sure_start_data(struct kobject *attr_name_kobj) 129 + { 130 + bioscfg_drv.sure_start_attr_kobj = attr_name_kobj; 131 + return sysfs_create_group(attr_name_kobj, &sure_start_attr_group); 132 + }
+112 -6
drivers/platform/x86/ideapad-laptop.c
··· 10 10 11 11 #include <linux/acpi.h> 12 12 #include <linux/backlight.h> 13 + #include <linux/bitfield.h> 13 14 #include <linux/bitops.h> 14 15 #include <linux/bug.h> 15 16 #include <linux/debugfs.h> ··· 86 85 SALS_FNLOCK_OFF = 0xf, 87 86 }; 88 87 88 + /* 89 + * These correspond to the number of supported states - 1 90 + * Future keyboard types may need a new system, if there's a collision 91 + * KBD_BL_TRISTATE_AUTO has no way to report or set the auto state 92 + * so it effectively has 3 states, but needs to handle 4 93 + */ 94 + enum { 95 + KBD_BL_STANDARD = 1, 96 + KBD_BL_TRISTATE = 2, 97 + KBD_BL_TRISTATE_AUTO = 3, 98 + }; 99 + 100 + #define KBD_BL_QUERY_TYPE 0x1 101 + #define KBD_BL_TRISTATE_TYPE 0x5 102 + #define KBD_BL_TRISTATE_AUTO_TYPE 0x7 103 + 104 + #define KBD_BL_COMMAND_GET 0x2 105 + #define KBD_BL_COMMAND_SET 0x3 106 + #define KBD_BL_COMMAND_TYPE GENMASK(7, 4) 107 + 108 + #define KBD_BL_GET_BRIGHTNESS GENMASK(15, 1) 109 + #define KBD_BL_SET_BRIGHTNESS GENMASK(19, 16) 110 + 111 + #define KBD_BL_KBLC_CHANGED_EVENT 12 112 + 89 113 struct ideapad_dytc_priv { 90 114 enum platform_profile_option current_profile; 91 115 struct platform_profile_handler pprof; ··· 148 122 } features; 149 123 struct { 150 124 bool initialized; 125 + int type; 151 126 struct led_classdev led; 152 127 unsigned int last_brightness; 153 128 } kbd_bl; ··· 267 240 static int exec_sals(acpi_handle handle, unsigned long arg) 268 241 { 269 242 return exec_simple_method(handle, "SALS", arg); 243 + } 244 + 245 + static int exec_kblc(acpi_handle handle, unsigned long arg) 246 + { 247 + return exec_simple_method(handle, "KBLC", arg); 248 + } 249 + 250 + static int eval_kblc(acpi_handle handle, unsigned long cmd, unsigned long *res) 251 + { 252 + return eval_int_with_arg(handle, "KBLC", cmd, res); 270 253 } 271 254 272 255 static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res) ··· 1312 1275 /* 1313 1276 * keyboard backlight 1314 1277 */ 1278 + static int ideapad_kbd_bl_check_tristate(int type) 1279 + { 1280 + return (type == KBD_BL_TRISTATE) || (type == KBD_BL_TRISTATE_AUTO); 1281 + } 1282 + 1315 1283 static int ideapad_kbd_bl_brightness_get(struct ideapad_private *priv) 1316 1284 { 1317 - unsigned long hals; 1285 + unsigned long value; 1318 1286 int err; 1319 1287 1320 - err = eval_hals(priv->adev->handle, &hals); 1288 + if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) { 1289 + err = eval_kblc(priv->adev->handle, 1290 + FIELD_PREP(KBD_BL_COMMAND_TYPE, priv->kbd_bl.type) | 1291 + KBD_BL_COMMAND_GET, 1292 + &value); 1293 + 1294 + if (err) 1295 + return err; 1296 + 1297 + /* Convert returned value to brightness level */ 1298 + value = FIELD_GET(KBD_BL_GET_BRIGHTNESS, value); 1299 + 1300 + /* Off, low or high */ 1301 + if (value <= priv->kbd_bl.led.max_brightness) 1302 + return value; 1303 + 1304 + /* Auto, report as off */ 1305 + if (value == priv->kbd_bl.led.max_brightness + 1) 1306 + return 0; 1307 + 1308 + /* Unknown value */ 1309 + dev_warn(&priv->platform_device->dev, 1310 + "Unknown keyboard backlight value: %lu", value); 1311 + return -EINVAL; 1312 + } 1313 + 1314 + err = eval_hals(priv->adev->handle, &value); 1321 1315 if (err) 1322 1316 return err; 1323 1317 1324 - return !!test_bit(HALS_KBD_BL_STATE_BIT, &hals); 1318 + return !!test_bit(HALS_KBD_BL_STATE_BIT, &value); 1325 1319 } 1326 1320 1327 1321 static enum led_brightness ideapad_kbd_bl_led_cdev_brightness_get(struct led_classdev *led_cdev) ··· 1364 1296 1365 1297 static int ideapad_kbd_bl_brightness_set(struct ideapad_private *priv, unsigned int brightness) 1366 1298 { 1367 - int err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF); 1299 + int err; 1300 + unsigned long value; 1301 + int type = priv->kbd_bl.type; 1302 + 1303 + if (ideapad_kbd_bl_check_tristate(type)) { 1304 + if (brightness > priv->kbd_bl.led.max_brightness) 1305 + return -EINVAL; 1306 + 1307 + value = FIELD_PREP(KBD_BL_SET_BRIGHTNESS, brightness) | 1308 + FIELD_PREP(KBD_BL_COMMAND_TYPE, type) | 1309 + KBD_BL_COMMAND_SET; 1310 + err = exec_kblc(priv->adev->handle, value); 1311 + } else { 1312 + err = exec_sals(priv->adev->handle, brightness ? SALS_KBD_BL_ON : SALS_KBD_BL_OFF); 1313 + } 1368 1314 1369 1315 if (err) 1370 1316 return err; ··· 1431 1349 1432 1350 priv->kbd_bl.last_brightness = brightness; 1433 1351 1352 + if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) { 1353 + priv->kbd_bl.led.max_brightness = 2; 1354 + } else { 1355 + priv->kbd_bl.led.max_brightness = 1; 1356 + } 1357 + 1434 1358 priv->kbd_bl.led.name = "platform::" LED_FUNCTION_KBD_BACKLIGHT; 1435 - priv->kbd_bl.led.max_brightness = 1; 1436 1359 priv->kbd_bl.led.brightness_get = ideapad_kbd_bl_led_cdev_brightness_get; 1437 1360 priv->kbd_bl.led.brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set; 1438 1361 priv->kbd_bl.led.flags = LED_BRIGHT_HW_CHANGED; ··· 1548 1461 case 2: 1549 1462 ideapad_backlight_notify_power(priv); 1550 1463 break; 1464 + case KBD_BL_KBLC_CHANGED_EVENT: 1551 1465 case 1: 1552 1466 /* 1553 1467 * Some IdeaPads report event 1 every ~20 ··· 1650 1562 if (test_bit(HALS_FNLOCK_SUPPORT_BIT, &val)) 1651 1563 priv->features.fn_lock = true; 1652 1564 1653 - if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val)) 1565 + if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val)) { 1654 1566 priv->features.kbd_bl = true; 1567 + priv->kbd_bl.type = KBD_BL_STANDARD; 1568 + } 1655 1569 1656 1570 if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val)) 1657 1571 priv->features.usb_charging = true; 1572 + } 1573 + } 1574 + 1575 + if (acpi_has_method(handle, "KBLC")) { 1576 + if (!eval_kblc(priv->adev->handle, KBD_BL_QUERY_TYPE, &val)) { 1577 + if (val == KBD_BL_TRISTATE_TYPE) { 1578 + priv->features.kbd_bl = true; 1579 + priv->kbd_bl.type = KBD_BL_TRISTATE; 1580 + } else if (val == KBD_BL_TRISTATE_AUTO_TYPE) { 1581 + priv->features.kbd_bl = true; 1582 + priv->kbd_bl.type = KBD_BL_TRISTATE_AUTO; 1583 + } else { 1584 + dev_warn(&priv->platform_device->dev, 1585 + "Unknown keyboard type: %lu", 1586 + val); 1587 + } 1658 1588 } 1659 1589 } 1660 1590 }
+392 -7
drivers/platform/x86/intel/tpmi.c
··· 47 47 */ 48 48 49 49 #include <linux/auxiliary_bus.h> 50 + #include <linux/bitfield.h> 51 + #include <linux/debugfs.h> 52 + #include <linux/delay.h> 50 53 #include <linux/intel_tpmi.h> 51 54 #include <linux/io.h> 55 + #include <linux/iopoll.h> 52 56 #include <linux/module.h> 53 57 #include <linux/pci.h> 58 + #include <linux/security.h> 59 + #include <linux/sizes.h> 60 + #include <linux/string_helpers.h> 54 61 55 62 #include "vsec.h" 56 63 ··· 90 83 * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially 91 84 * this offset = "Address" from VSEC header + PFS Capability 92 85 * offset for this feature entry. 86 + * @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device 93 87 * 94 88 * Represents TPMI instance information for one TPMI ID. 95 89 */ 96 90 struct intel_tpmi_pm_feature { 97 91 struct intel_tpmi_pfs_entry pfs_header; 98 92 unsigned int vsec_offset; 93 + struct intel_vsec_device *vsec_dev; 99 94 }; 100 95 101 96 /** ··· 107 98 * @feature_count: Number of TPMI of TPMI instances pointed by tpmi_features 108 99 * @pfs_start: Start of PFS offset for the TPMI instances in this device 109 100 * @plat_info: Stores platform info which can be used by the client drivers 101 + * @tpmi_control_mem: Memory mapped IO for getting control information 102 + * @dbgfs_dir: debugfs entry pointer 110 103 * 111 104 * Stores the information for all TPMI devices enumerated from a single PCI device. 112 105 */ ··· 118 107 int feature_count; 119 108 u64 pfs_start; 120 109 struct intel_tpmi_plat_info plat_info; 110 + void __iomem *tpmi_control_mem; 111 + struct dentry *dbgfs_dir; 121 112 }; 122 113 123 114 /** ··· 152 139 TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */ 153 140 TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */ 154 141 TPMI_ID_SST = 5, /* Speed Select Technology */ 142 + TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */ 155 143 TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ 156 144 }; 145 + 146 + /* 147 + * The size from hardware is in u32 units. This size is from a trusted hardware, 148 + * but better to verify for pre silicon platforms. Set size to 0, when invalid. 149 + */ 150 + #define TPMI_GET_SINGLE_ENTRY_SIZE(pfs) \ 151 + ({ \ 152 + pfs->pfs_header.entry_size > SZ_1K ? 0 : pfs->pfs_header.entry_size << 2; \ 153 + }) 157 154 158 155 /* Used during auxbus device creation */ 159 156 static DEFINE_IDA(intel_vsec_tpmi_ida); ··· 197 174 return NULL; 198 175 } 199 176 EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI); 177 + 178 + /* TPMI Control Interface */ 179 + 180 + #define TPMI_CONTROL_STATUS_OFFSET 0x00 181 + #define TPMI_COMMAND_OFFSET 0x08 182 + 183 + /* 184 + * Spec is calling for max 1 seconds to get ownership at the worst 185 + * case. Read at 10 ms timeouts and repeat up to 1 second. 186 + */ 187 + #define TPMI_CONTROL_TIMEOUT_US (10 * USEC_PER_MSEC) 188 + #define TPMI_CONTROL_TIMEOUT_MAX_US (1 * USEC_PER_SEC) 189 + 190 + #define TPMI_RB_TIMEOUT_US (10 * USEC_PER_MSEC) 191 + #define TPMI_RB_TIMEOUT_MAX_US USEC_PER_SEC 192 + 193 + /* TPMI Control status register defines */ 194 + 195 + #define TPMI_CONTROL_STATUS_RB BIT_ULL(0) 196 + 197 + #define TPMI_CONTROL_STATUS_OWNER GENMASK_ULL(5, 4) 198 + #define TPMI_OWNER_NONE 0 199 + #define TPMI_OWNER_IN_BAND 1 200 + 201 + #define TPMI_CONTROL_STATUS_CPL BIT_ULL(6) 202 + #define TPMI_CONTROL_STATUS_RESULT GENMASK_ULL(15, 8) 203 + #define TPMI_CONTROL_STATUS_LEN GENMASK_ULL(31, 16) 204 + 205 + #define TPMI_CMD_PKT_LEN 2 206 + #define TPMI_CMD_STATUS_SUCCESS 0x40 207 + 208 + /* TPMI command data registers */ 209 + #define TMPI_CONTROL_DATA_CMD GENMASK_ULL(7, 0) 210 + #define TMPI_CONTROL_DATA_VAL GENMASK_ULL(63, 32) 211 + #define TPMI_CONTROL_DATA_VAL_FEATURE GENMASK_ULL(48, 40) 212 + 213 + /* Command to send via control interface */ 214 + #define TPMI_CONTROL_GET_STATE_CMD 0x10 215 + 216 + #define TPMI_CONTROL_CMD_MASK GENMASK_ULL(48, 40) 217 + 218 + #define TPMI_CMD_LEN_MASK GENMASK_ULL(18, 16) 219 + 220 + #define TPMI_STATE_DISABLED BIT_ULL(0) 221 + #define TPMI_STATE_LOCKED BIT_ULL(31) 222 + 223 + /* Mutex to complete get feature status without interruption */ 224 + static DEFINE_MUTEX(tpmi_dev_lock); 225 + 226 + static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner) 227 + { 228 + u64 control; 229 + 230 + return readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET, 231 + control, owner == FIELD_GET(TPMI_CONTROL_STATUS_OWNER, control), 232 + TPMI_CONTROL_TIMEOUT_US, TPMI_CONTROL_TIMEOUT_MAX_US); 233 + } 234 + 235 + static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id, 236 + int *locked, int *disabled) 237 + { 238 + u64 control, data; 239 + int ret; 240 + 241 + if (!tpmi_info->tpmi_control_mem) 242 + return -EFAULT; 243 + 244 + mutex_lock(&tpmi_dev_lock); 245 + 246 + /* Wait for owner bit set to 0 (none) */ 247 + ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_NONE); 248 + if (ret) 249 + goto err_unlock; 250 + 251 + /* set command id to 0x10 for TPMI_GET_STATE */ 252 + data = FIELD_PREP(TMPI_CONTROL_DATA_CMD, TPMI_CONTROL_GET_STATE_CMD); 253 + 254 + /* 32 bits for DATA offset and +8 for feature_id field */ 255 + data |= FIELD_PREP(TPMI_CONTROL_DATA_VAL_FEATURE, feature_id); 256 + 257 + /* Write at command offset for qword access */ 258 + writeq(data, tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET); 259 + 260 + /* Wait for owner bit set to in-band */ 261 + ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_IN_BAND); 262 + if (ret) 263 + goto err_unlock; 264 + 265 + /* Set Run Busy and packet length of 2 dwords */ 266 + control = TPMI_CONTROL_STATUS_RB; 267 + control |= FIELD_PREP(TPMI_CONTROL_STATUS_LEN, TPMI_CMD_PKT_LEN); 268 + 269 + /* Write at status offset for qword access */ 270 + writeq(control, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET); 271 + 272 + /* Wait for Run Busy clear */ 273 + ret = readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET, 274 + control, !(control & TPMI_CONTROL_STATUS_RB), 275 + TPMI_RB_TIMEOUT_US, TPMI_RB_TIMEOUT_MAX_US); 276 + if (ret) 277 + goto done_proc; 278 + 279 + control = FIELD_GET(TPMI_CONTROL_STATUS_RESULT, control); 280 + if (control != TPMI_CMD_STATUS_SUCCESS) { 281 + ret = -EBUSY; 282 + goto done_proc; 283 + } 284 + 285 + /* Response is ready */ 286 + data = readq(tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET); 287 + data = FIELD_GET(TMPI_CONTROL_DATA_VAL, data); 288 + 289 + *disabled = 0; 290 + *locked = 0; 291 + 292 + if (!(data & TPMI_STATE_DISABLED)) 293 + *disabled = 1; 294 + 295 + if (data & TPMI_STATE_LOCKED) 296 + *locked = 1; 297 + 298 + ret = 0; 299 + 300 + done_proc: 301 + /* Set CPL "completion" bit */ 302 + writeq(TPMI_CONTROL_STATUS_CPL, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET); 303 + 304 + err_unlock: 305 + mutex_unlock(&tpmi_dev_lock); 306 + 307 + return ret; 308 + } 309 + 310 + int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, 311 + int *locked, int *disabled) 312 + { 313 + struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent); 314 + struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev); 315 + 316 + return tpmi_read_feature_status(tpmi_info, feature_id, locked, disabled); 317 + } 318 + EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI); 319 + 320 + static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) 321 + { 322 + struct intel_tpmi_info *tpmi_info = s->private; 323 + struct intel_tpmi_pm_feature *pfs; 324 + int locked, disabled, ret, i; 325 + 326 + seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start); 327 + seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\n"); 328 + for (i = 0; i < tpmi_info->feature_count; ++i) { 329 + pfs = &tpmi_info->tpmi_features[i]; 330 + ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &locked, 331 + &disabled); 332 + if (ret) { 333 + locked = 'U'; 334 + disabled = 'U'; 335 + } else { 336 + disabled = disabled ? 'Y' : 'N'; 337 + locked = locked ? 'Y' : 'N'; 338 + } 339 + seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\n", 340 + pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries, 341 + pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset, 342 + pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled); 343 + } 344 + 345 + return 0; 346 + } 347 + DEFINE_SHOW_ATTRIBUTE(tpmi_pfs_dbg); 348 + 349 + #define MEM_DUMP_COLUMN_COUNT 8 350 + 351 + static int tpmi_mem_dump_show(struct seq_file *s, void *unused) 352 + { 353 + size_t row_size = MEM_DUMP_COLUMN_COUNT * sizeof(u32); 354 + struct intel_tpmi_pm_feature *pfs = s->private; 355 + int count, ret = 0; 356 + void __iomem *mem; 357 + u32 off, size; 358 + u8 *buffer; 359 + 360 + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); 361 + if (!size) 362 + return -EIO; 363 + 364 + buffer = kmalloc(size, GFP_KERNEL); 365 + if (!buffer) 366 + return -ENOMEM; 367 + 368 + off = pfs->vsec_offset; 369 + 370 + mutex_lock(&tpmi_dev_lock); 371 + 372 + for (count = 0; count < pfs->pfs_header.num_entries; ++count) { 373 + seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off); 374 + 375 + mem = ioremap(off, size); 376 + if (!mem) { 377 + ret = -ENOMEM; 378 + break; 379 + } 380 + 381 + memcpy_fromio(buffer, mem, size); 382 + 383 + seq_hex_dump(s, " ", DUMP_PREFIX_OFFSET, row_size, sizeof(u32), buffer, size, 384 + false); 385 + 386 + iounmap(mem); 387 + 388 + off += size; 389 + } 390 + 391 + mutex_unlock(&tpmi_dev_lock); 392 + 393 + kfree(buffer); 394 + 395 + return ret; 396 + } 397 + DEFINE_SHOW_ATTRIBUTE(tpmi_mem_dump); 398 + 399 + static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos) 400 + { 401 + struct seq_file *m = file->private_data; 402 + struct intel_tpmi_pm_feature *pfs = m->private; 403 + u32 addr, value, punit, size; 404 + u32 num_elems, *array; 405 + void __iomem *mem; 406 + int ret; 407 + 408 + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); 409 + if (!size) 410 + return -EIO; 411 + 412 + ret = parse_int_array_user(userbuf, len, (int **)&array); 413 + if (ret < 0) 414 + return ret; 415 + 416 + num_elems = *array; 417 + if (num_elems != 3) { 418 + ret = -EINVAL; 419 + goto exit_write; 420 + } 421 + 422 + punit = array[1]; 423 + addr = array[2]; 424 + value = array[3]; 425 + 426 + if (punit >= pfs->pfs_header.num_entries) { 427 + ret = -EINVAL; 428 + goto exit_write; 429 + } 430 + 431 + if (addr >= size) { 432 + ret = -EINVAL; 433 + goto exit_write; 434 + } 435 + 436 + mutex_lock(&tpmi_dev_lock); 437 + 438 + mem = ioremap(pfs->vsec_offset + punit * size, size); 439 + if (!mem) { 440 + ret = -ENOMEM; 441 + goto unlock_mem_write; 442 + } 443 + 444 + writel(value, mem + addr); 445 + 446 + iounmap(mem); 447 + 448 + ret = len; 449 + 450 + unlock_mem_write: 451 + mutex_unlock(&tpmi_dev_lock); 452 + 453 + exit_write: 454 + kfree(array); 455 + 456 + return ret; 457 + } 458 + 459 + static int mem_write_show(struct seq_file *s, void *unused) 460 + { 461 + return 0; 462 + } 463 + 464 + static int mem_write_open(struct inode *inode, struct file *file) 465 + { 466 + return single_open(file, mem_write_show, inode->i_private); 467 + } 468 + 469 + static const struct file_operations mem_write_ops = { 470 + .open = mem_write_open, 471 + .read = seq_read, 472 + .write = mem_write, 473 + .llseek = seq_lseek, 474 + .release = single_release, 475 + }; 476 + 477 + #define tpmi_to_dev(info) (&info->vsec_dev->pcidev->dev) 478 + 479 + static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) 480 + { 481 + char name[64]; 482 + int i; 483 + 484 + snprintf(name, sizeof(name), "tpmi-%s", dev_name(tpmi_to_dev(tpmi_info))); 485 + tpmi_info->dbgfs_dir = debugfs_create_dir(name, NULL); 486 + 487 + debugfs_create_file("pfs_dump", 0444, tpmi_info->dbgfs_dir, tpmi_info, &tpmi_pfs_dbg_fops); 488 + 489 + for (i = 0; i < tpmi_info->feature_count; ++i) { 490 + struct intel_tpmi_pm_feature *pfs; 491 + struct dentry *dir; 492 + 493 + pfs = &tpmi_info->tpmi_features[i]; 494 + snprintf(name, sizeof(name), "tpmi-id-%02x", pfs->pfs_header.tpmi_id); 495 + dir = debugfs_create_dir(name, tpmi_info->dbgfs_dir); 496 + 497 + debugfs_create_file("mem_dump", 0444, dir, pfs, &tpmi_mem_dump_fops); 498 + debugfs_create_file("mem_write", 0644, dir, pfs, &mem_write_ops); 499 + } 500 + } 501 + 502 + static void tpmi_set_control_base(struct auxiliary_device *auxdev, 503 + struct intel_tpmi_info *tpmi_info, 504 + struct intel_tpmi_pm_feature *pfs) 505 + { 506 + void __iomem *mem; 507 + u32 size; 508 + 509 + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); 510 + if (!size) 511 + return; 512 + 513 + mem = devm_ioremap(&auxdev->dev, pfs->vsec_offset, size); 514 + if (!mem) 515 + return; 516 + 517 + /* mem is pointing to TPMI CONTROL base */ 518 + tpmi_info->tpmi_control_mem = mem; 519 + } 200 520 201 521 static const char *intel_tpmi_name(enum intel_tpmi_id id) 202 522 { ··· 682 316 struct pci_dev *pci_dev = vsec_dev->pcidev; 683 317 struct intel_tpmi_info *tpmi_info; 684 318 u64 pfs_start = 0; 685 - int i; 319 + int ret, i; 686 320 687 321 tpmi_info = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_info), GFP_KERNEL); 688 322 if (!tpmi_info) ··· 705 339 int size, ret; 706 340 707 341 pfs = &tpmi_info->tpmi_features[i]; 342 + pfs->vsec_dev = vsec_dev; 708 343 709 344 res = &vsec_dev->resource[i]; 710 345 if (!res) ··· 734 367 */ 735 368 if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID) 736 369 tpmi_process_info(tpmi_info, pfs); 370 + 371 + if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID) 372 + tpmi_set_control_base(auxdev, tpmi_info, pfs); 737 373 } 738 374 739 375 tpmi_info->pfs_start = pfs_start; 740 376 741 377 auxiliary_set_drvdata(auxdev, tpmi_info); 742 378 743 - return tpmi_create_devices(tpmi_info); 379 + ret = tpmi_create_devices(tpmi_info); 380 + if (ret) 381 + return ret; 382 + 383 + /* 384 + * Allow debugfs when security policy allows. Everything this debugfs 385 + * interface provides, can also be done via /dev/mem access. If 386 + * /dev/mem interface is locked, don't allow debugfs to present any 387 + * information. Also check for CAP_SYS_RAWIO as /dev/mem interface. 388 + */ 389 + if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO)) 390 + tpmi_dbgfs_register(tpmi_info); 391 + 392 + return 0; 744 393 } 745 394 746 395 static int tpmi_probe(struct auxiliary_device *auxdev, ··· 765 382 return intel_vsec_tpmi_init(auxdev); 766 383 } 767 384 768 - /* 769 - * Remove callback is not needed currently as there is no 770 - * cleanup required. All memory allocs are device managed. All 771 - * devices created by this modules are also device managed. 772 - */ 385 + static void tpmi_remove(struct auxiliary_device *auxdev) 386 + { 387 + struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(auxdev); 388 + 389 + debugfs_remove_recursive(tpmi_info->dbgfs_dir); 390 + } 773 391 774 392 static const struct auxiliary_device_id tpmi_id_table[] = { 775 393 { .name = "intel_vsec.tpmi" }, ··· 781 397 static struct auxiliary_driver tpmi_aux_driver = { 782 398 .id_table = tpmi_id_table, 783 399 .probe = tpmi_probe, 400 + .remove = tpmi_remove, 784 401 }; 785 402 786 403 module_auxiliary_driver(tpmi_aux_driver);
+331 -43
drivers/platform/x86/mlx-platform.c
··· 12 12 #include <linux/i2c-mux.h> 13 13 #include <linux/io.h> 14 14 #include <linux/module.h> 15 + #include <linux/pci.h> 15 16 #include <linux/platform_device.h> 16 17 #include <linux/platform_data/i2c-mux-reg.h> 17 18 #include <linux/platform_data/mlxreg.h> ··· 36 35 #define MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET 0x09 37 36 #define MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET 0x0a 38 37 #define MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET 0x0b 38 + #define MLXPLAT_CPLD_LPC_REG_RESET_GP1_OFFSET 0x17 39 39 #define MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET 0x19 40 40 #define MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET 0x1c 41 41 #define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET 0x1d ··· 64 62 #define MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET 0x37 65 63 #define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a 66 64 #define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b 65 + #define MLXPLAT_CPLD_LPC_REG_FU_CAP_OFFSET 0x3c 67 66 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40 68 67 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET 0x41 69 68 #define MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET 0x42 ··· 97 94 #define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET 0x88 98 95 #define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET 0x89 99 96 #define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET 0x8a 97 + #define MLXPLAT_CPLD_LPC_REG_CPLD5_VER_OFFSET 0x8e 98 + #define MLXPLAT_CPLD_LPC_REG_CPLD5_PN_OFFSET 0x8f 99 + #define MLXPLAT_CPLD_LPC_REG_CPLD5_PN1_OFFSET 0x90 100 100 #define MLXPLAT_CPLD_LPC_REG_EROT_OFFSET 0x91 101 101 #define MLXPLAT_CPLD_LPC_REG_EROT_EVENT_OFFSET 0x92 102 102 #define MLXPLAT_CPLD_LPC_REG_EROT_MASK_OFFSET 0x93 ··· 134 128 #define MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET 0xb9 135 129 #define MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET 0xc2 136 130 #define MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT 0xc3 131 + #define MLXPLAT_CPLD_LPC_REG_CPLD5_MVER_OFFSET 0xc4 137 132 #define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET 0xc7 138 133 #define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET 0xc8 139 134 #define MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET 0xc9 ··· 243 236 #define MLXPLAT_CPLD_VOLTREG_UPD_MASK GENMASK(5, 4) 244 237 #define MLXPLAT_CPLD_GWP_MASK GENMASK(0, 0) 245 238 #define MLXPLAT_CPLD_EROT_MASK GENMASK(1, 0) 239 + #define MLXPLAT_CPLD_FU_CAP_MASK GENMASK(1, 0) 246 240 #define MLXPLAT_CPLD_PWR_BUTTON_MASK BIT(0) 247 241 #define MLXPLAT_CPLD_LATCH_RST_MASK BIT(6) 248 242 #define MLXPLAT_CPLD_THERMAL1_PDB_MASK BIT(3) ··· 256 248 MLXPLAT_CPLD_PWM_PG_MASK) 257 249 #define MLXPLAT_CPLD_I2C_CAP_BIT 0x04 258 250 #define MLXPLAT_CPLD_I2C_CAP_MASK GENMASK(5, MLXPLAT_CPLD_I2C_CAP_BIT) 251 + #define MLXPLAT_CPLD_SYS_RESET_MASK BIT(0) 259 252 260 253 /* Masks for aggregation for comex carriers */ 261 254 #define MLXPLAT_CPLD_AGGR_MASK_CARRIER BIT(1) ··· 268 259 #define MLXPLAT_CPLD_LPC_LC_MASK GENMASK(7, 0) 269 260 270 261 #define MLXPLAT_CPLD_HALT_MASK BIT(3) 262 + #define MLXPLAT_CPLD_RESET_MASK GENMASK(7, 1) 271 263 272 264 /* Default I2C parent bus number */ 273 265 #define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1 ··· 332 322 #define MLXPLAT_I2C_MAIN_BUS_NOTIFIED 0x01 333 323 #define MLXPLAT_I2C_MAIN_BUS_HANDLE_CREATED 0x02 334 324 325 + /* Lattice FPGA PCI configuration */ 326 + #define PCI_VENDOR_ID_LATTICE 0x1204 327 + #define PCI_DEVICE_ID_LATTICE_I2C_BRIDGE 0x9c2f 328 + #define PCI_DEVICE_ID_LATTICE_JTAG_BRIDGE 0x9c30 329 + #define PCI_DEVICE_ID_LATTICE_LPC_BRIDGE 0x9c32 330 + 335 331 /* mlxplat_priv - platform private data 336 332 * @pdev_i2c - i2c controller platform device 337 333 * @pdev_mux - array of mux platform devices ··· 350 334 * @hotplug_resources: system hotplug resources 351 335 * @hotplug_resources_size: size of system hotplug resources 352 336 * @hi2c_main_init_status: init status of I2C main bus 337 + * @irq_fpga: FPGA IRQ number 353 338 */ 354 339 struct mlxplat_priv { 355 340 struct platform_device *pdev_i2c; ··· 364 347 struct resource *hotplug_resources; 365 348 unsigned int hotplug_resources_size; 366 349 u8 i2c_main_init_status; 350 + int irq_fpga; 367 351 }; 368 352 369 353 static struct platform_device *mlxplat_dev; 370 354 static int mlxplat_i2c_main_complition_notify(void *handle, int id); 355 + static void __iomem *i2c_bridge_addr, *jtag_bridge_addr; 371 356 372 357 /* Regions for LPC I2C controller and LPC base register space */ 373 358 static const struct resource mlxplat_lpc_resources[] = { ··· 454 435 static int mlxplat_max_adap_num; 455 436 static int mlxplat_mux_num; 456 437 static struct i2c_mux_reg_platform_data *mlxplat_mux_data; 438 + static struct notifier_block *mlxplat_reboot_nb; 457 439 458 440 /* Platform extended mux data */ 459 441 static struct i2c_mux_reg_platform_data mlxplat_extended_mux_data[] = { ··· 2375 2355 mlxplat_mlxcpld_l1_switch_pwr_events_handler(void *handle, enum mlxreg_hotplug_kind kind, 2376 2356 u8 action) 2377 2357 { 2378 - dev_info(&mlxplat_dev->dev, "System shutdown due to short press of power button"); 2379 - kernel_power_off(); 2358 + if (action) { 2359 + dev_info(&mlxplat_dev->dev, "System shutdown due to short press of power button"); 2360 + kernel_power_off(); 2361 + } 2362 + 2380 2363 return 0; 2381 2364 } 2382 2365 ··· 2394 2371 .reg = MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET, 2395 2372 .mask = MLXPLAT_CPLD_PWR_BUTTON_MASK, 2396 2373 .hpdev.nr = MLXPLAT_CPLD_NR_NONE, 2374 + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, 2397 2375 .hpdev.notifier = &mlxplat_mlxcpld_l1_switch_pwr_events_notifier, 2398 2376 }, 2399 2377 }; ··· 2455 2431 .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, 2456 2432 .mask = MLXPLAT_CPLD_INTRUSION_MASK, 2457 2433 .hpdev.nr = MLXPLAT_CPLD_NR_NONE, 2434 + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, 2458 2435 .hpdev.notifier = &mlxplat_mlxcpld_l1_switch_intrusion_events_notifier, 2459 2436 }, 2460 2437 { ··· 3453 3428 .mode = 0444, 3454 3429 }, 3455 3430 { 3431 + .label = "cpld5_version", 3432 + .reg = MLXPLAT_CPLD_LPC_REG_CPLD5_VER_OFFSET, 3433 + .bit = GENMASK(7, 0), 3434 + .mode = 0444, 3435 + }, 3436 + { 3456 3437 .label = "cpld1_pn", 3457 3438 .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET, 3458 3439 .bit = GENMASK(15, 0), ··· 3487 3456 .regnum = 2, 3488 3457 }, 3489 3458 { 3459 + .label = "cpld5_pn", 3460 + .reg = MLXPLAT_CPLD_LPC_REG_CPLD5_PN_OFFSET, 3461 + .bit = GENMASK(15, 0), 3462 + .mode = 0444, 3463 + .regnum = 2, 3464 + }, 3465 + { 3490 3466 .label = "cpld1_version_min", 3491 3467 .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET, 3492 3468 .bit = GENMASK(7, 0), ··· 3514 3476 { 3515 3477 .label = "cpld4_version_min", 3516 3478 .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET, 3479 + .bit = GENMASK(7, 0), 3480 + .mode = 0444, 3481 + }, 3482 + { 3483 + .label = "cpld5_version_min", 3484 + .reg = MLXPLAT_CPLD_LPC_REG_CPLD5_MVER_OFFSET, 3517 3485 .bit = GENMASK(7, 0), 3518 3486 .mode = 0444, 3519 3487 }, ··· 3599 3555 .mode = 0444, 3600 3556 }, 3601 3557 { 3602 - .label = "reset_from_comex", 3558 + .label = "reset_swb_dc_dc_pwr_fail", 3603 3559 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, 3604 - .mask = GENMASK(7, 0) & ~BIT(4), 3560 + .mask = GENMASK(7, 0) & ~BIT(3), 3605 3561 .mode = 0444, 3606 3562 }, 3607 3563 { ··· 3620 3576 .label = "reset_asic_thermal", 3621 3577 .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, 3622 3578 .mask = GENMASK(7, 0) & ~BIT(7), 3579 + .mode = 0444, 3580 + }, 3581 + { 3582 + .label = "reset_sw_reset", 3583 + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, 3584 + .mask = GENMASK(7, 0) & ~BIT(0), 3623 3585 .mode = 0444, 3624 3586 }, 3625 3587 { ··· 3729 3679 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, 3730 3680 .mask = GENMASK(7, 0) & ~BIT(6), 3731 3681 .mode = 0200, 3682 + }, 3683 + { 3684 + .label = "jtag_cap", 3685 + .reg = MLXPLAT_CPLD_LPC_REG_FU_CAP_OFFSET, 3686 + .mask = MLXPLAT_CPLD_FU_CAP_MASK, 3687 + .bit = 1, 3688 + .mode = 0444, 3732 3689 }, 3733 3690 { 3734 3691 .label = "jtag_enable", ··· 3847 3790 .label = "erot2_ap_reset", 3848 3791 .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, 3849 3792 .mask = GENMASK(7, 0) & ~BIT(1), 3793 + .mode = 0444, 3794 + }, 3795 + { 3796 + .label = "lid_open", 3797 + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, 3798 + .mask = GENMASK(7, 0) & ~BIT(2), 3850 3799 .mode = 0444, 3851 3800 }, 3852 3801 { ··· 4495 4432 .mode = 0444, 4496 4433 }, 4497 4434 { 4435 + .label = "reset_long_pwr_pb", 4436 + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, 4437 + .mask = GENMASK(7, 0) & ~BIT(7), 4438 + .mode = 0444, 4439 + }, 4440 + { 4498 4441 .label = "pwr_cycle", 4499 4442 .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, 4500 4443 .mask = GENMASK(7, 0) & ~BIT(2), ··· 4974 4905 static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) 4975 4906 { 4976 4907 switch (reg) { 4908 + case MLXPLAT_CPLD_LPC_REG_RESET_GP1_OFFSET: 4977 4909 case MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET: 4978 4910 case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET: 4979 4911 case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET: ··· 4993 4923 case MLXPLAT_CPLD_LPC_SAFE_BIOS_OFFSET: 4994 4924 case MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET: 4995 4925 case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: 4926 + case MLXPLAT_CPLD_LPC_REG_FU_CAP_OFFSET: 4996 4927 case MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET: 4997 4928 case MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET: 4998 4929 case MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET: ··· 5072 5001 case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET: 5073 5002 case MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET: 5074 5003 case MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET: 5004 + case MLXPLAT_CPLD_LPC_REG_CPLD5_VER_OFFSET: 5075 5005 case MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET: 5076 5006 case MLXPLAT_CPLD_LPC_REG_CPLD1_PN1_OFFSET: 5077 5007 case MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET: ··· 5081 5009 case MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET: 5082 5010 case MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET: 5083 5011 case MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET: 5012 + case MLXPLAT_CPLD_LPC_REG_CPLD5_PN_OFFSET: 5013 + case MLXPLAT_CPLD_LPC_REG_CPLD5_PN1_OFFSET: 5014 + case MLXPLAT_CPLD_LPC_REG_RESET_GP1_OFFSET: 5084 5015 case MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET: 5085 5016 case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET: 5086 5017 case MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET: ··· 5109 5034 case MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET: 5110 5035 case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET: 5111 5036 case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: 5037 + case MLXPLAT_CPLD_LPC_REG_FU_CAP_OFFSET: 5112 5038 case MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET: 5113 5039 case MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET: 5114 5040 case MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET: ··· 5195 5119 case MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET: 5196 5120 case MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET: 5197 5121 case MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET: 5122 + case MLXPLAT_CPLD_LPC_REG_CPLD5_MVER_OFFSET: 5198 5123 case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: 5199 5124 case MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET: 5200 5125 case MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET: ··· 5237 5160 case MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET: 5238 5161 case MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET: 5239 5162 case MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET: 5163 + case MLXPLAT_CPLD_LPC_REG_CPLD5_VER_OFFSET: 5240 5164 case MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET: 5241 5165 case MLXPLAT_CPLD_LPC_REG_CPLD1_PN1_OFFSET: 5242 5166 case MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET: ··· 5246 5168 case MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET: 5247 5169 case MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET: 5248 5170 case MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET: 5171 + case MLXPLAT_CPLD_LPC_REG_CPLD5_PN_OFFSET: 5172 + case MLXPLAT_CPLD_LPC_REG_CPLD5_PN1_OFFSET: 5173 + case MLXPLAT_CPLD_LPC_REG_RESET_GP1_OFFSET: 5249 5174 case MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET: 5250 5175 case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET: 5251 5176 case MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET: ··· 5272 5191 case MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET: 5273 5192 case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET: 5274 5193 case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: 5194 + case MLXPLAT_CPLD_LPC_REG_FU_CAP_OFFSET: 5275 5195 case MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET: 5276 5196 case MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET: 5277 5197 case MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET: ··· 5352 5270 case MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET: 5353 5271 case MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET: 5354 5272 case MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET: 5273 + case MLXPLAT_CPLD_LPC_REG_CPLD5_MVER_OFFSET: 5355 5274 case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: 5356 5275 case MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET: 5357 5276 case MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET: ··· 5552 5469 static struct mlxreg_core_platform_data 5553 5470 *mlxplat_wd_data[MLXPLAT_CPLD_WD_MAX_DEVS]; 5554 5471 static const struct regmap_config *mlxplat_regmap_config; 5472 + static struct pci_dev *lpc_bridge; 5473 + static struct pci_dev *i2c_bridge; 5474 + static struct pci_dev *jtag_bridge; 5475 + 5476 + /* Platform default reset function */ 5477 + static int mlxplat_reboot_notifier(struct notifier_block *nb, unsigned long action, void *unused) 5478 + { 5479 + struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); 5480 + u32 regval; 5481 + int ret; 5482 + 5483 + ret = regmap_read(priv->regmap, MLXPLAT_CPLD_LPC_REG_RESET_GP1_OFFSET, &regval); 5484 + 5485 + if (action == SYS_RESTART && !ret && regval & MLXPLAT_CPLD_SYS_RESET_MASK) 5486 + regmap_write(priv->regmap, MLXPLAT_CPLD_LPC_REG_RESET_GP1_OFFSET, 5487 + MLXPLAT_CPLD_RESET_MASK); 5488 + 5489 + return NOTIFY_DONE; 5490 + } 5491 + 5492 + static struct notifier_block mlxplat_reboot_default_nb = { 5493 + .notifier_call = mlxplat_reboot_notifier, 5494 + }; 5555 5495 5556 5496 /* Platform default poweroff function */ 5557 5497 static void mlxplat_poweroff(void) 5558 5498 { 5559 5499 struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); 5560 5500 5501 + if (mlxplat_reboot_nb) 5502 + unregister_reboot_notifier(mlxplat_reboot_nb); 5561 5503 regmap_write(priv->regmap, MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, MLXPLAT_CPLD_HALT_MASK); 5504 + kernel_halt(); 5505 + } 5506 + 5507 + static int __init mlxplat_register_platform_device(void) 5508 + { 5509 + mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, -1, 5510 + mlxplat_lpc_resources, 5511 + ARRAY_SIZE(mlxplat_lpc_resources)); 5512 + if (IS_ERR(mlxplat_dev)) 5513 + return PTR_ERR(mlxplat_dev); 5514 + else 5515 + return 1; 5562 5516 } 5563 5517 5564 5518 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) ··· 5618 5498 mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; 5619 5499 mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; 5620 5500 5621 - return 1; 5501 + return mlxplat_register_platform_device(); 5622 5502 } 5623 5503 5624 5504 static int __init mlxplat_dmi_default_wc_matched(const struct dmi_system_id *dmi) ··· 5641 5521 mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; 5642 5522 mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; 5643 5523 5644 - return 1; 5524 + return mlxplat_register_platform_device(); 5645 5525 } 5646 5526 5647 5527 static int __init mlxplat_dmi_default_eth_wc_blade_matched(const struct dmi_system_id *dmi) ··· 5666 5546 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5667 5547 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng; 5668 5548 5669 - return 1; 5549 + return mlxplat_register_platform_device(); 5670 5550 } 5671 5551 5672 5552 static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) ··· 5689 5569 mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; 5690 5570 mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; 5691 5571 5692 - return 1; 5572 + return mlxplat_register_platform_device(); 5693 5573 } 5694 5574 5695 5575 static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) ··· 5712 5592 mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; 5713 5593 mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; 5714 5594 5715 - return 1; 5595 + return mlxplat_register_platform_device(); 5716 5596 } 5717 5597 5718 5598 static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) ··· 5735 5615 mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; 5736 5616 mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; 5737 5617 5738 - return 1; 5618 + return mlxplat_register_platform_device(); 5739 5619 } 5740 5620 5741 5621 static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) ··· 5761 5641 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5762 5642 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng; 5763 5643 5764 - return 1; 5644 + return mlxplat_register_platform_device(); 5765 5645 } 5766 5646 5767 5647 static int __init mlxplat_dmi_comex_matched(const struct dmi_system_id *dmi) ··· 5786 5666 mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; 5787 5667 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_comex; 5788 5668 5789 - return 1; 5669 + return mlxplat_register_platform_device(); 5790 5670 } 5791 5671 5792 5672 static int __init mlxplat_dmi_ng400_matched(const struct dmi_system_id *dmi) ··· 5812 5692 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5813 5693 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400; 5814 5694 5815 - return 1; 5695 + return mlxplat_register_platform_device(); 5816 5696 } 5817 5697 5818 5698 static int __init mlxplat_dmi_modular_matched(const struct dmi_system_id *dmi) ··· 5832 5712 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5833 5713 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_eth_modular; 5834 5714 5835 - return 1; 5715 + return mlxplat_register_platform_device(); 5836 5716 } 5837 5717 5838 5718 static int __init mlxplat_dmi_chassis_blade_matched(const struct dmi_system_id *dmi) ··· 5854 5734 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5855 5735 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400; 5856 5736 5857 - return 1; 5737 + return mlxplat_register_platform_device(); 5858 5738 } 5859 5739 5860 5740 static int __init mlxplat_dmi_rack_switch_matched(const struct dmi_system_id *dmi) ··· 5875 5755 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5876 5756 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_rack_switch; 5877 5757 5878 - return 1; 5758 + return mlxplat_register_platform_device(); 5879 5759 } 5880 5760 5881 5761 static int __init mlxplat_dmi_ng800_matched(const struct dmi_system_id *dmi) ··· 5896 5776 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5897 5777 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400; 5898 5778 5899 - return 1; 5779 + return mlxplat_register_platform_device(); 5900 5780 } 5901 5781 5902 5782 static int __init mlxplat_dmi_l1_switch_matched(const struct dmi_system_id *dmi) ··· 5917 5797 mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; 5918 5798 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_rack_switch; 5919 5799 pm_power_off = mlxplat_poweroff; 5800 + mlxplat_reboot_nb = &mlxplat_reboot_default_nb; 5920 5801 5921 - return 1; 5802 + return mlxplat_register_platform_device(); 5922 5803 } 5923 5804 5924 5805 static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { ··· 6163 6042 { 6164 6043 int err; 6165 6044 6166 - mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, PLATFORM_DEVID_NONE, 6167 - mlxplat_lpc_resources, 6168 - ARRAY_SIZE(mlxplat_lpc_resources)); 6169 - if (IS_ERR(mlxplat_dev)) 6170 - return PTR_ERR(mlxplat_dev); 6171 - 6172 6045 mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev, 6173 6046 mlxplat_lpc_resources[1].start, 1); 6174 6047 if (!mlxplat_mlxcpld_regmap_ctx.base) { ··· 6176 6061 return 0; 6177 6062 6178 6063 fail_devm_ioport_map: 6179 - platform_device_unregister(mlxplat_dev); 6180 6064 return err; 6181 6065 } 6182 6066 6183 6067 static void mlxplat_lpc_cpld_device_exit(void) 6184 6068 { 6185 - platform_device_unregister(mlxplat_dev); 6069 + } 6070 + 6071 + static int 6072 + mlxplat_pci_fpga_device_init(unsigned int device, const char *res_name, struct pci_dev **pci_bridge, 6073 + void __iomem **pci_bridge_addr) 6074 + { 6075 + void __iomem *pci_mem_addr; 6076 + struct pci_dev *pci_dev; 6077 + int err; 6078 + 6079 + pci_dev = pci_get_device(PCI_VENDOR_ID_LATTICE, device, NULL); 6080 + if (!pci_dev) 6081 + return -ENODEV; 6082 + 6083 + err = pci_enable_device(pci_dev); 6084 + if (err) { 6085 + dev_err(&pci_dev->dev, "pci_enable_device failed with error %d\n", err); 6086 + goto fail_pci_enable_device; 6087 + } 6088 + 6089 + err = pci_request_region(pci_dev, 0, res_name); 6090 + if (err) { 6091 + dev_err(&pci_dev->dev, "pci_request_regions failed with error %d\n", err); 6092 + goto fail_pci_request_regions; 6093 + } 6094 + 6095 + err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64)); 6096 + if (err) { 6097 + err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32)); 6098 + if (err) { 6099 + dev_err(&pci_dev->dev, "dma_set_mask failed with error %d\n", err); 6100 + goto fail_pci_set_dma_mask; 6101 + } 6102 + } 6103 + 6104 + pci_set_master(pci_dev); 6105 + 6106 + pci_mem_addr = devm_ioremap(&pci_dev->dev, pci_resource_start(pci_dev, 0), 6107 + pci_resource_len(pci_dev, 0)); 6108 + if (!pci_mem_addr) { 6109 + dev_err(&mlxplat_dev->dev, "ioremap failed\n"); 6110 + err = -EIO; 6111 + goto fail_ioremap; 6112 + } 6113 + 6114 + *pci_bridge = pci_dev; 6115 + *pci_bridge_addr = pci_mem_addr; 6116 + 6117 + return 0; 6118 + 6119 + fail_ioremap: 6120 + fail_pci_set_dma_mask: 6121 + pci_release_regions(pci_dev); 6122 + fail_pci_request_regions: 6123 + pci_disable_device(pci_dev); 6124 + fail_pci_enable_device: 6125 + return err; 6126 + } 6127 + 6128 + static void 6129 + mlxplat_pci_fpga_device_exit(struct pci_dev *pci_bridge, 6130 + void __iomem *pci_bridge_addr) 6131 + { 6132 + iounmap(pci_bridge_addr); 6133 + pci_release_regions(pci_bridge); 6134 + pci_disable_device(pci_bridge); 6135 + } 6136 + 6137 + static int 6138 + mlxplat_pci_fpga_devices_init(struct resource **hotplug_resources, 6139 + unsigned int *hotplug_resources_size) 6140 + { 6141 + int err; 6142 + 6143 + err = mlxplat_pci_fpga_device_init(PCI_DEVICE_ID_LATTICE_LPC_BRIDGE, 6144 + "mlxplat_lpc_bridge", &lpc_bridge, 6145 + &mlxplat_mlxcpld_regmap_ctx.base); 6146 + if (err) 6147 + goto mlxplat_pci_fpga_device_init_lpc_fail; 6148 + 6149 + err = mlxplat_pci_fpga_device_init(PCI_DEVICE_ID_LATTICE_I2C_BRIDGE, 6150 + "mlxplat_i2c_bridge", &i2c_bridge, 6151 + &i2c_bridge_addr); 6152 + if (err) 6153 + goto mlxplat_pci_fpga_device_init_i2c_fail; 6154 + 6155 + err = mlxplat_pci_fpga_device_init(PCI_DEVICE_ID_LATTICE_JTAG_BRIDGE, 6156 + "mlxplat_jtag_bridge", &jtag_bridge, 6157 + &jtag_bridge_addr); 6158 + if (err) 6159 + goto mlxplat_pci_fpga_device_init_jtag_fail; 6160 + 6161 + return 0; 6162 + 6163 + mlxplat_pci_fpga_device_init_jtag_fail: 6164 + mlxplat_pci_fpga_device_exit(i2c_bridge, i2c_bridge_addr); 6165 + mlxplat_pci_fpga_device_init_i2c_fail: 6166 + mlxplat_pci_fpga_device_exit(lpc_bridge, mlxplat_mlxcpld_regmap_ctx.base); 6167 + mlxplat_pci_fpga_device_init_lpc_fail: 6168 + return err; 6169 + } 6170 + 6171 + static void mlxplat_pci_fpga_devices_exit(void) 6172 + { 6173 + mlxplat_pci_fpga_device_exit(jtag_bridge, jtag_bridge_addr); 6174 + mlxplat_pci_fpga_device_exit(i2c_bridge, i2c_bridge_addr); 6175 + mlxplat_pci_fpga_device_exit(lpc_bridge, mlxplat_mlxcpld_regmap_ctx.base); 6186 6176 } 6187 6177 6188 6178 static int 6189 6179 mlxplat_pre_init(struct resource **hotplug_resources, unsigned int *hotplug_resources_size) 6190 6180 { 6191 - return mlxplat_lpc_cpld_device_init(hotplug_resources, hotplug_resources_size); 6181 + int err; 6182 + 6183 + err = mlxplat_pci_fpga_devices_init(hotplug_resources, hotplug_resources_size); 6184 + if (err == -ENODEV) 6185 + return mlxplat_lpc_cpld_device_init(hotplug_resources, hotplug_resources_size); 6186 + 6187 + return err; 6192 6188 } 6193 6189 6194 6190 static void mlxplat_post_exit(void) 6195 6191 { 6196 - mlxplat_lpc_cpld_device_exit(); 6192 + if (lpc_bridge) 6193 + mlxplat_pci_fpga_devices_exit(); 6194 + else 6195 + mlxplat_lpc_cpld_device_exit(); 6197 6196 } 6198 6197 6199 6198 static int mlxplat_post_init(struct mlxplat_priv *priv) ··· 6317 6088 /* Add hotplug driver */ 6318 6089 if (mlxplat_hotplug) { 6319 6090 mlxplat_hotplug->regmap = priv->regmap; 6091 + if (priv->irq_fpga) 6092 + mlxplat_hotplug->irq = priv->irq_fpga; 6320 6093 priv->pdev_hotplug = 6321 6094 platform_device_register_resndata(&mlxplat_dev->dev, 6322 6095 "mlxreg-hotplug", PLATFORM_DEVID_NONE, ··· 6432 6201 return mlxplat_post_init(priv); 6433 6202 } 6434 6203 6435 - static int mlxplat_i2c_mux_topolgy_init(struct mlxplat_priv *priv) 6204 + static int mlxplat_i2c_mux_topology_init(struct mlxplat_priv *priv) 6436 6205 { 6437 6206 int i, err; 6438 6207 ··· 6461 6230 return err; 6462 6231 } 6463 6232 6464 - static void mlxplat_i2c_mux_topolgy_exit(struct mlxplat_priv *priv) 6233 + static void mlxplat_i2c_mux_topology_exit(struct mlxplat_priv *priv) 6465 6234 { 6466 6235 int i; 6467 6236 ··· 6475 6244 { 6476 6245 struct mlxplat_priv *priv = handle; 6477 6246 6478 - return mlxplat_i2c_mux_topolgy_init(priv); 6247 + return mlxplat_i2c_mux_topology_init(priv); 6479 6248 } 6480 6249 6481 6250 static int mlxplat_i2c_main_init(struct mlxplat_priv *priv) ··· 6493 6262 mlxplat_i2c->regmap = priv->regmap; 6494 6263 mlxplat_i2c->handle = priv; 6495 6264 6265 + /* Set mapped base address of I2C-LPC bridge over PCIe */ 6266 + if (lpc_bridge) 6267 + mlxplat_i2c->addr = i2c_bridge_addr; 6496 6268 priv->pdev_i2c = platform_device_register_resndata(&mlxplat_dev->dev, "i2c_mlxcpld", 6497 6269 nr, priv->hotplug_resources, 6498 6270 priv->hotplug_resources_size, ··· 6506 6272 } 6507 6273 6508 6274 if (priv->i2c_main_init_status == MLXPLAT_I2C_MAIN_BUS_NOTIFIED) { 6509 - err = mlxplat_i2c_mux_topolgy_init(priv); 6275 + err = mlxplat_i2c_mux_topology_init(priv); 6510 6276 if (err) 6511 - goto fail_mlxplat_i2c_mux_topolgy_init; 6277 + goto fail_mlxplat_i2c_mux_topology_init; 6512 6278 } 6513 6279 6514 6280 return 0; 6515 6281 6516 - fail_mlxplat_i2c_mux_topolgy_init: 6282 + fail_mlxplat_i2c_mux_topology_init: 6517 6283 fail_platform_i2c_register: 6518 6284 fail_mlxplat_mlxcpld_verify_bus_topology: 6519 6285 return err; ··· 6521 6287 6522 6288 static void mlxplat_i2c_main_exit(struct mlxplat_priv *priv) 6523 6289 { 6524 - mlxplat_i2c_mux_topolgy_exit(priv); 6290 + mlxplat_i2c_mux_topology_exit(priv); 6525 6291 if (priv->pdev_i2c) 6526 6292 platform_device_unregister(priv->pdev_i2c); 6527 6293 } 6528 6294 6529 - static int __init mlxplat_init(void) 6295 + static int mlxplat_probe(struct platform_device *pdev) 6530 6296 { 6531 - unsigned int hotplug_resources_size; 6532 - struct resource *hotplug_resources; 6297 + unsigned int hotplug_resources_size = 0; 6298 + struct resource *hotplug_resources = NULL; 6299 + struct acpi_device *acpi_dev; 6533 6300 struct mlxplat_priv *priv; 6534 - int i, err; 6301 + int irq_fpga = 0, i, err; 6535 6302 6536 - if (!dmi_check_system(mlxplat_dmi_table)) 6537 - return -ENODEV; 6303 + acpi_dev = ACPI_COMPANION(&pdev->dev); 6304 + if (acpi_dev) { 6305 + irq_fpga = acpi_dev_gpio_irq_get(acpi_dev, 0); 6306 + if (irq_fpga < 0) 6307 + return -ENODEV; 6308 + mlxplat_dev = pdev; 6309 + } 6538 6310 6539 6311 err = mlxplat_pre_init(&hotplug_resources, &hotplug_resources_size); 6540 6312 if (err) ··· 6555 6315 platform_set_drvdata(mlxplat_dev, priv); 6556 6316 priv->hotplug_resources = hotplug_resources; 6557 6317 priv->hotplug_resources_size = hotplug_resources_size; 6318 + priv->irq_fpga = irq_fpga; 6558 6319 6559 6320 if (!mlxplat_regmap_config) 6560 6321 mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config; ··· 6587 6346 if (err) 6588 6347 goto fail_regcache_sync; 6589 6348 6349 + if (mlxplat_reboot_nb) { 6350 + err = register_reboot_notifier(mlxplat_reboot_nb); 6351 + if (err) 6352 + goto fail_register_reboot_notifier; 6353 + } 6354 + 6590 6355 return 0; 6591 6356 6357 + fail_register_reboot_notifier: 6592 6358 fail_regcache_sync: 6593 6359 mlxplat_pre_exit(priv); 6594 6360 fail_mlxplat_i2c_main_init: ··· 6605 6357 6606 6358 return err; 6607 6359 } 6608 - module_init(mlxplat_init); 6609 6360 6610 - static void __exit mlxplat_exit(void) 6361 + static int mlxplat_remove(struct platform_device *pdev) 6611 6362 { 6612 6363 struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); 6613 6364 6614 6365 if (pm_power_off) 6615 6366 pm_power_off = NULL; 6367 + if (mlxplat_reboot_nb) 6368 + unregister_reboot_notifier(mlxplat_reboot_nb); 6616 6369 mlxplat_pre_exit(priv); 6617 6370 mlxplat_i2c_main_exit(priv); 6618 6371 mlxplat_post_exit(); 6372 + return 0; 6373 + } 6374 + 6375 + static const struct acpi_device_id mlxplat_acpi_table[] = { 6376 + { "MLNXBF49", 0 }, 6377 + {} 6378 + }; 6379 + MODULE_DEVICE_TABLE(acpi, mlxplat_acpi_table); 6380 + 6381 + static struct platform_driver mlxplat_driver = { 6382 + .driver = { 6383 + .name = "mlxplat", 6384 + .acpi_match_table = mlxplat_acpi_table, 6385 + .probe_type = PROBE_FORCE_SYNCHRONOUS, 6386 + }, 6387 + .probe = mlxplat_probe, 6388 + .remove = mlxplat_remove, 6389 + }; 6390 + 6391 + static int __init mlxplat_init(void) 6392 + { 6393 + int err; 6394 + 6395 + if (!dmi_check_system(mlxplat_dmi_table)) 6396 + return -ENODEV; 6397 + 6398 + err = platform_driver_register(&mlxplat_driver); 6399 + if (err) 6400 + return err; 6401 + return 0; 6402 + } 6403 + module_init(mlxplat_init); 6404 + 6405 + static void __exit mlxplat_exit(void) 6406 + { 6407 + if (mlxplat_dev) 6408 + platform_device_unregister(mlxplat_dev); 6409 + 6410 + platform_driver_unregister(&mlxplat_driver); 6619 6411 } 6620 6412 module_exit(mlxplat_exit); 6621 6413
+251
drivers/platform/x86/sel3350-platform.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause 2 + /* 3 + * Copyright 2023 Schweitzer Engineering Laboratories, Inc. 4 + * 2350 NE Hopkins Court, Pullman, WA 99163 USA 5 + * 6 + * Platform support for the b2093 mainboard used in SEL-3350 computers. 7 + * Consumes GPIO from the SoC to provide standard LED and power supply 8 + * devices. 9 + */ 10 + 11 + #include <linux/acpi.h> 12 + #include <linux/gpio/consumer.h> 13 + #include <linux/gpio/machine.h> 14 + #include <linux/leds.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/power_supply.h> 18 + 19 + /* Broxton communities */ 20 + #define BXT_NW "INT3452:01" 21 + #define BXT_W "INT3452:02" 22 + #define BXT_SW "INT3452:03" 23 + 24 + #define B2093_GPIO_ACPI_ID "SEL0003" 25 + 26 + #define SEL_PS_A "sel_ps_a" 27 + #define SEL_PS_A_DETECT "sel_ps_a_detect" 28 + #define SEL_PS_A_GOOD "sel_ps_a_good" 29 + #define SEL_PS_B "sel_ps_b" 30 + #define SEL_PS_B_DETECT "sel_ps_b_detect" 31 + #define SEL_PS_B_GOOD "sel_ps_b_good" 32 + 33 + /* LEDs */ 34 + static const struct gpio_led sel3350_leds[] = { 35 + { .name = "sel:green:aux1" }, 36 + { .name = "sel:green:aux2" }, 37 + { .name = "sel:green:aux3" }, 38 + { .name = "sel:green:aux4" }, 39 + { .name = "sel:red:alarm" }, 40 + { .name = "sel:green:enabled", 41 + .default_state = LEDS_GPIO_DEFSTATE_ON }, 42 + { .name = "sel:red:aux1" }, 43 + { .name = "sel:red:aux2" }, 44 + { .name = "sel:red:aux3" }, 45 + { .name = "sel:red:aux4" }, 46 + }; 47 + 48 + static const struct gpio_led_platform_data sel3350_leds_pdata = { 49 + .num_leds = ARRAY_SIZE(sel3350_leds), 50 + .leds = sel3350_leds, 51 + }; 52 + 53 + /* Map GPIOs to LEDs */ 54 + static struct gpiod_lookup_table sel3350_leds_table = { 55 + .dev_id = "leds-gpio", 56 + .table = { 57 + GPIO_LOOKUP_IDX(BXT_NW, 49, NULL, 0, GPIO_ACTIVE_HIGH), 58 + GPIO_LOOKUP_IDX(BXT_NW, 50, NULL, 1, GPIO_ACTIVE_HIGH), 59 + GPIO_LOOKUP_IDX(BXT_NW, 51, NULL, 2, GPIO_ACTIVE_HIGH), 60 + GPIO_LOOKUP_IDX(BXT_NW, 52, NULL, 3, GPIO_ACTIVE_HIGH), 61 + GPIO_LOOKUP_IDX(BXT_W, 20, NULL, 4, GPIO_ACTIVE_HIGH), 62 + GPIO_LOOKUP_IDX(BXT_W, 21, NULL, 5, GPIO_ACTIVE_HIGH), 63 + GPIO_LOOKUP_IDX(BXT_SW, 37, NULL, 6, GPIO_ACTIVE_HIGH), 64 + GPIO_LOOKUP_IDX(BXT_SW, 38, NULL, 7, GPIO_ACTIVE_HIGH), 65 + GPIO_LOOKUP_IDX(BXT_SW, 39, NULL, 8, GPIO_ACTIVE_HIGH), 66 + GPIO_LOOKUP_IDX(BXT_SW, 40, NULL, 9, GPIO_ACTIVE_HIGH), 67 + {}, 68 + } 69 + }; 70 + 71 + /* Map GPIOs to power supplies */ 72 + static struct gpiod_lookup_table sel3350_gpios_table = { 73 + .dev_id = B2093_GPIO_ACPI_ID ":00", 74 + .table = { 75 + GPIO_LOOKUP(BXT_NW, 44, SEL_PS_A_DETECT, GPIO_ACTIVE_LOW), 76 + GPIO_LOOKUP(BXT_NW, 45, SEL_PS_A_GOOD, GPIO_ACTIVE_LOW), 77 + GPIO_LOOKUP(BXT_NW, 46, SEL_PS_B_DETECT, GPIO_ACTIVE_LOW), 78 + GPIO_LOOKUP(BXT_NW, 47, SEL_PS_B_GOOD, GPIO_ACTIVE_LOW), 79 + {}, 80 + } 81 + }; 82 + 83 + /* Power Supplies */ 84 + 85 + struct sel3350_power_cfg_data { 86 + struct gpio_desc *ps_detect; 87 + struct gpio_desc *ps_good; 88 + }; 89 + 90 + static int sel3350_power_get_property(struct power_supply *psy, 91 + enum power_supply_property psp, 92 + union power_supply_propval *val) 93 + { 94 + struct sel3350_power_cfg_data *data = power_supply_get_drvdata(psy); 95 + 96 + switch (psp) { 97 + case POWER_SUPPLY_PROP_HEALTH: 98 + if (gpiod_get_value(data->ps_detect)) { 99 + if (gpiod_get_value(data->ps_good)) 100 + val->intval = POWER_SUPPLY_HEALTH_GOOD; 101 + else 102 + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 103 + } else { 104 + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 105 + } 106 + break; 107 + case POWER_SUPPLY_PROP_PRESENT: 108 + val->intval = gpiod_get_value(data->ps_detect); 109 + break; 110 + case POWER_SUPPLY_PROP_ONLINE: 111 + val->intval = gpiod_get_value(data->ps_good); 112 + break; 113 + default: 114 + return -EINVAL; 115 + } 116 + return 0; 117 + } 118 + 119 + static const enum power_supply_property sel3350_power_properties[] = { 120 + POWER_SUPPLY_PROP_HEALTH, 121 + POWER_SUPPLY_PROP_PRESENT, 122 + POWER_SUPPLY_PROP_ONLINE, 123 + }; 124 + 125 + static const struct power_supply_desc sel3350_ps_a_desc = { 126 + .name = SEL_PS_A, 127 + .type = POWER_SUPPLY_TYPE_MAINS, 128 + .properties = sel3350_power_properties, 129 + .num_properties = ARRAY_SIZE(sel3350_power_properties), 130 + .get_property = sel3350_power_get_property, 131 + }; 132 + 133 + static const struct power_supply_desc sel3350_ps_b_desc = { 134 + .name = SEL_PS_B, 135 + .type = POWER_SUPPLY_TYPE_MAINS, 136 + .properties = sel3350_power_properties, 137 + .num_properties = ARRAY_SIZE(sel3350_power_properties), 138 + .get_property = sel3350_power_get_property, 139 + }; 140 + 141 + struct sel3350_data { 142 + struct platform_device *leds_pdev; 143 + struct power_supply *ps_a; 144 + struct power_supply *ps_b; 145 + struct sel3350_power_cfg_data ps_a_cfg_data; 146 + struct sel3350_power_cfg_data ps_b_cfg_data; 147 + }; 148 + 149 + static int sel3350_probe(struct platform_device *pdev) 150 + { 151 + int rs; 152 + struct sel3350_data *sel3350; 153 + struct power_supply_config ps_cfg = {}; 154 + 155 + sel3350 = devm_kzalloc(&pdev->dev, sizeof(struct sel3350_data), GFP_KERNEL); 156 + if (!sel3350) 157 + return -ENOMEM; 158 + 159 + platform_set_drvdata(pdev, sel3350); 160 + 161 + gpiod_add_lookup_table(&sel3350_leds_table); 162 + gpiod_add_lookup_table(&sel3350_gpios_table); 163 + 164 + sel3350->leds_pdev = platform_device_register_data( 165 + NULL, 166 + "leds-gpio", 167 + PLATFORM_DEVID_NONE, 168 + &sel3350_leds_pdata, 169 + sizeof(sel3350_leds_pdata)); 170 + if (IS_ERR(sel3350->leds_pdev)) { 171 + rs = PTR_ERR(sel3350->leds_pdev); 172 + dev_err(&pdev->dev, "Failed registering platform device: %d\n", rs); 173 + goto err_platform; 174 + } 175 + 176 + /* Power Supply A */ 177 + sel3350->ps_a_cfg_data.ps_detect = devm_gpiod_get(&pdev->dev, 178 + SEL_PS_A_DETECT, 179 + GPIOD_IN); 180 + sel3350->ps_a_cfg_data.ps_good = devm_gpiod_get(&pdev->dev, 181 + SEL_PS_A_GOOD, 182 + GPIOD_IN); 183 + ps_cfg.drv_data = &sel3350->ps_a_cfg_data; 184 + sel3350->ps_a = devm_power_supply_register(&pdev->dev, 185 + &sel3350_ps_a_desc, 186 + &ps_cfg); 187 + if (IS_ERR(sel3350->ps_a)) { 188 + rs = PTR_ERR(sel3350->ps_a); 189 + dev_err(&pdev->dev, "Failed registering power supply A: %d\n", rs); 190 + goto err_ps; 191 + } 192 + 193 + /* Power Supply B */ 194 + sel3350->ps_b_cfg_data.ps_detect = devm_gpiod_get(&pdev->dev, 195 + SEL_PS_B_DETECT, 196 + GPIOD_IN); 197 + sel3350->ps_b_cfg_data.ps_good = devm_gpiod_get(&pdev->dev, 198 + SEL_PS_B_GOOD, 199 + GPIOD_IN); 200 + ps_cfg.drv_data = &sel3350->ps_b_cfg_data; 201 + sel3350->ps_b = devm_power_supply_register(&pdev->dev, 202 + &sel3350_ps_b_desc, 203 + &ps_cfg); 204 + if (IS_ERR(sel3350->ps_b)) { 205 + rs = PTR_ERR(sel3350->ps_b); 206 + dev_err(&pdev->dev, "Failed registering power supply B: %d\n", rs); 207 + goto err_ps; 208 + } 209 + 210 + return 0; 211 + 212 + err_ps: 213 + platform_device_unregister(sel3350->leds_pdev); 214 + err_platform: 215 + gpiod_remove_lookup_table(&sel3350_gpios_table); 216 + gpiod_remove_lookup_table(&sel3350_leds_table); 217 + 218 + return rs; 219 + } 220 + 221 + static int sel3350_remove(struct platform_device *pdev) 222 + { 223 + struct sel3350_data *sel3350 = platform_get_drvdata(pdev); 224 + 225 + platform_device_unregister(sel3350->leds_pdev); 226 + gpiod_remove_lookup_table(&sel3350_gpios_table); 227 + gpiod_remove_lookup_table(&sel3350_leds_table); 228 + 229 + return 0; 230 + } 231 + 232 + static const struct acpi_device_id sel3350_device_ids[] = { 233 + { B2093_GPIO_ACPI_ID, 0 }, 234 + { "", 0 }, 235 + }; 236 + MODULE_DEVICE_TABLE(acpi, sel3350_device_ids); 237 + 238 + static struct platform_driver sel3350_platform_driver = { 239 + .probe = sel3350_probe, 240 + .remove = sel3350_remove, 241 + .driver = { 242 + .name = "sel3350-platform", 243 + .acpi_match_table = sel3350_device_ids, 244 + }, 245 + }; 246 + module_platform_driver(sel3350_platform_driver); 247 + 248 + MODULE_AUTHOR("Schweitzer Engineering Laboratories"); 249 + MODULE_DESCRIPTION("SEL-3350 platform driver"); 250 + MODULE_LICENSE("Dual BSD/GPL"); 251 + MODULE_SOFTDEP("pre: pinctrl_broxton leds-gpio");
+64
drivers/platform/x86/siemens/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Siemens X86 Platform Specific Drivers 4 + # 5 + 6 + config SIEMENS_SIMATIC_IPC 7 + tristate "Siemens Simatic IPC Class driver" 8 + help 9 + This Simatic IPC class driver is the central of several drivers. It 10 + is mainly used for system identification, after which drivers in other 11 + classes will take care of driving specifics of those machines. 12 + i.e. LEDs and watchdog. 13 + 14 + To compile this driver as a module, choose M here: the module 15 + will be called simatic-ipc. 16 + 17 + config SIEMENS_SIMATIC_IPC_BATT 18 + tristate "CMOS battery driver for Siemens Simatic IPCs" 19 + default SIEMENS_SIMATIC_IPC 20 + depends on HWMON 21 + depends on SIEMENS_SIMATIC_IPC 22 + help 23 + This option enables support for monitoring the voltage of the CMOS 24 + batteries of several Industrial PCs from Siemens. 25 + 26 + To compile this driver as a module, choose M here: the module 27 + will be called simatic-ipc-batt. 28 + 29 + config SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE 30 + tristate "CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO" 31 + default SIEMENS_SIMATIC_IPC_BATT 32 + depends on PINCTRL_BROXTON 33 + depends on SIEMENS_SIMATIC_IPC_BATT 34 + help 35 + This option enables CMOS battery monitoring for Simatic Industrial PCs 36 + from Siemens based on Apollo Lake GPIO. 37 + 38 + To compile this driver as a module, choose M here: the module 39 + will be called simatic-ipc-batt-apollolake. 40 + 41 + config SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE 42 + tristate "CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO" 43 + default SIEMENS_SIMATIC_IPC_BATT 44 + depends on PINCTRL_ELKHARTLAKE 45 + depends on SIEMENS_SIMATIC_IPC_BATT 46 + help 47 + This option enables CMOS battery monitoring for Simatic Industrial PCs 48 + from Siemens based on Elkhart Lake GPIO. 49 + 50 + To compile this driver as a module, choose M here: the module 51 + will be called simatic-ipc-batt-elkhartlake. 52 + 53 + config SIEMENS_SIMATIC_IPC_BATT_F7188X 54 + tristate "CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO" 55 + default SIEMENS_SIMATIC_IPC_BATT 56 + depends on GPIO_F7188X 57 + depends on PINCTRL_ALDERLAKE 58 + depends on SIEMENS_SIMATIC_IPC_BATT 59 + help 60 + This option enables CMOS battery monitoring for Simatic Industrial PCs 61 + from Siemens based on Nuvoton GPIO. 62 + 63 + To compile this driver as a module, choose M here: the module 64 + will be called simatic-ipc-batt-f7188x.
+11
drivers/platform/x86/siemens/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for linux/drivers/platform/x86/siemens 4 + # Siemens x86 Platform-Specific Drivers 5 + # 6 + 7 + obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o 8 + obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT) += simatic-ipc-batt.o 9 + obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_APOLLOLAKE) += simatic-ipc-batt-apollolake.o 10 + obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_ELKHARTLAKE) += simatic-ipc-batt-elkhartlake.o 11 + obj-$(CONFIG_SIEMENS_SIMATIC_IPC_BATT_F7188X) += simatic-ipc-batt-f7188x.o
+51
drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Siemens SIMATIC IPC driver for CMOS battery monitoring 4 + * 5 + * Copyright (c) Siemens AG, 2023 6 + * 7 + * Authors: 8 + * Henning Schild <henning.schild@siemens.com> 9 + */ 10 + 11 + #include <linux/gpio/machine.h> 12 + #include <linux/gpio/consumer.h> 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + 17 + #include "simatic-ipc-batt.h" 18 + 19 + static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_127e = { 20 + .table = { 21 + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 55, NULL, 0, GPIO_ACTIVE_HIGH), 22 + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 61, NULL, 1, GPIO_ACTIVE_HIGH), 23 + GPIO_LOOKUP_IDX("apollolake-pinctrl.1", 41, NULL, 2, GPIO_ACTIVE_HIGH), 24 + {} /* Terminating entry */ 25 + }, 26 + }; 27 + 28 + static int simatic_ipc_batt_apollolake_remove(struct platform_device *pdev) 29 + { 30 + return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_127e); 31 + } 32 + 33 + static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev) 34 + { 35 + return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_127e); 36 + } 37 + 38 + static struct platform_driver simatic_ipc_batt_driver = { 39 + .probe = simatic_ipc_batt_apollolake_probe, 40 + .remove = simatic_ipc_batt_apollolake_remove, 41 + .driver = { 42 + .name = KBUILD_MODNAME, 43 + }, 44 + }; 45 + 46 + module_platform_driver(simatic_ipc_batt_driver); 47 + 48 + MODULE_LICENSE("GPL"); 49 + MODULE_ALIAS("platform:" KBUILD_MODNAME); 50 + MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl"); 51 + MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
+51
drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Siemens SIMATIC IPC driver for CMOS battery monitoring 4 + * 5 + * Copyright (c) Siemens AG, 2023 6 + * 7 + * Authors: 8 + * Henning Schild <henning.schild@siemens.com> 9 + */ 10 + 11 + #include <linux/gpio/machine.h> 12 + #include <linux/gpio/consumer.h> 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + 17 + #include "simatic-ipc-batt.h" 18 + 19 + static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_21a = { 20 + .table = { 21 + GPIO_LOOKUP_IDX("INTC1020:04", 18, NULL, 0, GPIO_ACTIVE_HIGH), 22 + GPIO_LOOKUP_IDX("INTC1020:04", 19, NULL, 1, GPIO_ACTIVE_HIGH), 23 + GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH), 24 + {} /* Terminating entry */ 25 + }, 26 + }; 27 + 28 + static int simatic_ipc_batt_elkhartlake_remove(struct platform_device *pdev) 29 + { 30 + return simatic_ipc_batt_remove(pdev, &simatic_ipc_batt_gpio_table_bx_21a); 31 + } 32 + 33 + static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev) 34 + { 35 + return simatic_ipc_batt_probe(pdev, &simatic_ipc_batt_gpio_table_bx_21a); 36 + } 37 + 38 + static struct platform_driver simatic_ipc_batt_driver = { 39 + .probe = simatic_ipc_batt_elkhartlake_probe, 40 + .remove = simatic_ipc_batt_elkhartlake_remove, 41 + .driver = { 42 + .name = KBUILD_MODNAME, 43 + }, 44 + }; 45 + 46 + module_platform_driver(simatic_ipc_batt_driver); 47 + 48 + MODULE_LICENSE("GPL"); 49 + MODULE_ALIAS("platform:" KBUILD_MODNAME); 50 + MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl"); 51 + MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
+87
drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Siemens SIMATIC IPC driver for CMOS battery monitoring 4 + * 5 + * Copyright (c) Siemens AG, 2023 6 + * 7 + * Authors: 8 + * Henning Schild <henning.schild@siemens.com> 9 + */ 10 + 11 + #include <linux/gpio/machine.h> 12 + #include <linux/gpio/consumer.h> 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/platform_data/x86/simatic-ipc-base.h> 17 + 18 + #include "simatic-ipc-batt.h" 19 + 20 + static struct gpiod_lookup_table *batt_lookup_table; 21 + 22 + static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_227g = { 23 + .table = { 24 + GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH), 25 + GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH), 26 + GPIO_LOOKUP_IDX("INTC1020:01", 66, NULL, 2, GPIO_ACTIVE_HIGH), 27 + {} /* Terminating entry */ 28 + }, 29 + }; 30 + 31 + static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_39a = { 32 + .table = { 33 + GPIO_LOOKUP_IDX("gpio-f7188x-6", 4, NULL, 0, GPIO_ACTIVE_HIGH), 34 + GPIO_LOOKUP_IDX("gpio-f7188x-6", 3, NULL, 1, GPIO_ACTIVE_HIGH), 35 + {} /* Terminating entry */ 36 + }, 37 + }; 38 + 39 + static struct gpiod_lookup_table simatic_ipc_batt_gpio_table_bx_59a = { 40 + .table = { 41 + GPIO_LOOKUP_IDX("gpio-f7188x-7", 6, NULL, 0, GPIO_ACTIVE_HIGH), 42 + GPIO_LOOKUP_IDX("gpio-f7188x-7", 5, NULL, 1, GPIO_ACTIVE_HIGH), 43 + GPIO_LOOKUP_IDX("INTC1056:00", 438, NULL, 2, GPIO_ACTIVE_HIGH), 44 + {} /* Terminating entry */ 45 + } 46 + }; 47 + 48 + static int simatic_ipc_batt_f7188x_remove(struct platform_device *pdev) 49 + { 50 + return simatic_ipc_batt_remove(pdev, batt_lookup_table); 51 + } 52 + 53 + static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev) 54 + { 55 + const struct simatic_ipc_platform *plat = pdev->dev.platform_data; 56 + 57 + switch (plat->devmode) { 58 + case SIMATIC_IPC_DEVICE_227G: 59 + batt_lookup_table = &simatic_ipc_batt_gpio_table_227g; 60 + break; 61 + case SIMATIC_IPC_DEVICE_BX_39A: 62 + batt_lookup_table = &simatic_ipc_batt_gpio_table_bx_39a; 63 + break; 64 + case SIMATIC_IPC_DEVICE_BX_59A: 65 + batt_lookup_table = &simatic_ipc_batt_gpio_table_bx_59a; 66 + break; 67 + default: 68 + return -ENODEV; 69 + } 70 + 71 + return simatic_ipc_batt_probe(pdev, batt_lookup_table); 72 + } 73 + 74 + static struct platform_driver simatic_ipc_batt_driver = { 75 + .probe = simatic_ipc_batt_f7188x_probe, 76 + .remove = simatic_ipc_batt_f7188x_remove, 77 + .driver = { 78 + .name = KBUILD_MODNAME, 79 + }, 80 + }; 81 + 82 + module_platform_driver(simatic_ipc_batt_driver); 83 + 84 + MODULE_LICENSE("GPL"); 85 + MODULE_ALIAS("platform:" KBUILD_MODNAME); 86 + MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl platform:alderlake-pinctrl"); 87 + MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
+253
drivers/platform/x86/siemens/simatic-ipc-batt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Siemens SIMATIC IPC driver for CMOS battery monitoring 4 + * 5 + * Copyright (c) Siemens AG, 2023 6 + * 7 + * Authors: 8 + * Gerd Haeussler <gerd.haeussler.ext@siemens.com> 9 + * Henning Schild <henning.schild@siemens.com> 10 + */ 11 + 12 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 + 14 + #include <linux/delay.h> 15 + #include <linux/io.h> 16 + #include <linux/ioport.h> 17 + #include <linux/gpio/machine.h> 18 + #include <linux/gpio/consumer.h> 19 + #include <linux/hwmon.h> 20 + #include <linux/hwmon-sysfs.h> 21 + #include <linux/jiffies.h> 22 + #include <linux/kernel.h> 23 + #include <linux/module.h> 24 + #include <linux/platform_device.h> 25 + #include <linux/platform_data/x86/simatic-ipc-base.h> 26 + #include <linux/sizes.h> 27 + 28 + #include "simatic-ipc-batt.h" 29 + 30 + #define BATT_DELAY_MS (1000 * 60 * 60 * 24) /* 24 h delay */ 31 + 32 + #define SIMATIC_IPC_BATT_LEVEL_FULL 3000 33 + #define SIMATIC_IPC_BATT_LEVEL_CRIT 2750 34 + #define SIMATIC_IPC_BATT_LEVEL_EMPTY 0 35 + 36 + static struct simatic_ipc_batt { 37 + u8 devmode; 38 + long current_state; 39 + struct gpio_desc *gpios[3]; 40 + unsigned long last_updated_jiffies; 41 + } priv; 42 + 43 + static long simatic_ipc_batt_read_gpio(void) 44 + { 45 + long r = SIMATIC_IPC_BATT_LEVEL_FULL; 46 + 47 + if (priv.gpios[2]) { 48 + gpiod_set_value(priv.gpios[2], 1); 49 + msleep(150); 50 + } 51 + 52 + if (gpiod_get_value_cansleep(priv.gpios[0])) 53 + r = SIMATIC_IPC_BATT_LEVEL_EMPTY; 54 + else if (gpiod_get_value_cansleep(priv.gpios[1])) 55 + r = SIMATIC_IPC_BATT_LEVEL_CRIT; 56 + 57 + if (priv.gpios[2]) 58 + gpiod_set_value(priv.gpios[2], 0); 59 + 60 + return r; 61 + } 62 + 63 + #define SIMATIC_IPC_BATT_PORT_BASE 0x404D 64 + static struct resource simatic_ipc_batt_io_res = 65 + DEFINE_RES_IO_NAMED(SIMATIC_IPC_BATT_PORT_BASE, SZ_1, KBUILD_MODNAME); 66 + 67 + static long simatic_ipc_batt_read_io(struct device *dev) 68 + { 69 + long r = SIMATIC_IPC_BATT_LEVEL_FULL; 70 + struct resource *res = &simatic_ipc_batt_io_res; 71 + u8 val; 72 + 73 + if (!request_muxed_region(res->start, resource_size(res), res->name)) { 74 + dev_err(dev, "Unable to register IO resource at %pR\n", res); 75 + return -EBUSY; 76 + } 77 + 78 + val = inb(SIMATIC_IPC_BATT_PORT_BASE); 79 + release_region(simatic_ipc_batt_io_res.start, resource_size(&simatic_ipc_batt_io_res)); 80 + 81 + if (val & (1 << 7)) 82 + r = SIMATIC_IPC_BATT_LEVEL_EMPTY; 83 + else if (val & (1 << 6)) 84 + r = SIMATIC_IPC_BATT_LEVEL_CRIT; 85 + 86 + return r; 87 + } 88 + 89 + static long simatic_ipc_batt_read_value(struct device *dev) 90 + { 91 + unsigned long next_update; 92 + 93 + next_update = priv.last_updated_jiffies + msecs_to_jiffies(BATT_DELAY_MS); 94 + if (time_after(jiffies, next_update) || !priv.last_updated_jiffies) { 95 + if (priv.devmode == SIMATIC_IPC_DEVICE_227E) 96 + priv.current_state = simatic_ipc_batt_read_io(dev); 97 + else 98 + priv.current_state = simatic_ipc_batt_read_gpio(); 99 + 100 + priv.last_updated_jiffies = jiffies; 101 + if (priv.current_state < SIMATIC_IPC_BATT_LEVEL_FULL) 102 + dev_warn(dev, "CMOS battery needs to be replaced.\n"); 103 + } 104 + 105 + return priv.current_state; 106 + } 107 + 108 + static int simatic_ipc_batt_read(struct device *dev, enum hwmon_sensor_types type, 109 + u32 attr, int channel, long *val) 110 + { 111 + switch (attr) { 112 + case hwmon_in_input: 113 + *val = simatic_ipc_batt_read_value(dev); 114 + break; 115 + case hwmon_in_lcrit: 116 + *val = SIMATIC_IPC_BATT_LEVEL_CRIT; 117 + break; 118 + default: 119 + return -EOPNOTSUPP; 120 + } 121 + 122 + return 0; 123 + } 124 + 125 + static umode_t simatic_ipc_batt_is_visible(const void *data, enum hwmon_sensor_types type, 126 + u32 attr, int channel) 127 + { 128 + if (attr == hwmon_in_input || attr == hwmon_in_lcrit) 129 + return 0444; 130 + 131 + return 0; 132 + } 133 + 134 + static const struct hwmon_ops simatic_ipc_batt_ops = { 135 + .is_visible = simatic_ipc_batt_is_visible, 136 + .read = simatic_ipc_batt_read, 137 + }; 138 + 139 + static const struct hwmon_channel_info *simatic_ipc_batt_info[] = { 140 + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LCRIT), 141 + NULL 142 + }; 143 + 144 + static const struct hwmon_chip_info simatic_ipc_batt_chip_info = { 145 + .ops = &simatic_ipc_batt_ops, 146 + .info = simatic_ipc_batt_info, 147 + }; 148 + 149 + int simatic_ipc_batt_remove(struct platform_device *pdev, struct gpiod_lookup_table *table) 150 + { 151 + gpiod_remove_lookup_table(table); 152 + return 0; 153 + } 154 + EXPORT_SYMBOL_GPL(simatic_ipc_batt_remove); 155 + 156 + int simatic_ipc_batt_probe(struct platform_device *pdev, struct gpiod_lookup_table *table) 157 + { 158 + struct simatic_ipc_platform *plat; 159 + struct device *dev = &pdev->dev; 160 + struct device *hwmon_dev; 161 + unsigned long flags; 162 + int err; 163 + 164 + plat = pdev->dev.platform_data; 165 + priv.devmode = plat->devmode; 166 + 167 + switch (priv.devmode) { 168 + case SIMATIC_IPC_DEVICE_127E: 169 + case SIMATIC_IPC_DEVICE_227G: 170 + case SIMATIC_IPC_DEVICE_BX_39A: 171 + case SIMATIC_IPC_DEVICE_BX_21A: 172 + case SIMATIC_IPC_DEVICE_BX_59A: 173 + table->dev_id = dev_name(dev); 174 + gpiod_add_lookup_table(table); 175 + break; 176 + case SIMATIC_IPC_DEVICE_227E: 177 + goto nogpio; 178 + default: 179 + return -ENODEV; 180 + } 181 + 182 + priv.gpios[0] = devm_gpiod_get_index(dev, "CMOSBattery empty", 0, GPIOD_IN); 183 + if (IS_ERR(priv.gpios[0])) { 184 + err = PTR_ERR(priv.gpios[0]); 185 + priv.gpios[0] = NULL; 186 + goto out; 187 + } 188 + priv.gpios[1] = devm_gpiod_get_index(dev, "CMOSBattery low", 1, GPIOD_IN); 189 + if (IS_ERR(priv.gpios[1])) { 190 + err = PTR_ERR(priv.gpios[1]); 191 + priv.gpios[1] = NULL; 192 + goto out; 193 + } 194 + 195 + if (table->table[2].key) { 196 + flags = GPIOD_OUT_HIGH; 197 + if (priv.devmode == SIMATIC_IPC_DEVICE_BX_21A || 198 + priv.devmode == SIMATIC_IPC_DEVICE_BX_59A) 199 + flags = GPIOD_OUT_LOW; 200 + priv.gpios[2] = devm_gpiod_get_index(dev, "CMOSBattery meter", 2, flags); 201 + if (IS_ERR(priv.gpios[2])) { 202 + err = PTR_ERR(priv.gpios[2]); 203 + priv.gpios[2] = NULL; 204 + goto out; 205 + } 206 + } else { 207 + priv.gpios[2] = NULL; 208 + } 209 + 210 + nogpio: 211 + hwmon_dev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME, 212 + &priv, 213 + &simatic_ipc_batt_chip_info, 214 + NULL); 215 + if (IS_ERR(hwmon_dev)) { 216 + err = PTR_ERR(hwmon_dev); 217 + goto out; 218 + } 219 + 220 + /* warn about aging battery even if userspace never reads hwmon */ 221 + simatic_ipc_batt_read_value(dev); 222 + 223 + return 0; 224 + out: 225 + simatic_ipc_batt_remove(pdev, table); 226 + 227 + return err; 228 + } 229 + EXPORT_SYMBOL_GPL(simatic_ipc_batt_probe); 230 + 231 + static int simatic_ipc_batt_io_remove(struct platform_device *pdev) 232 + { 233 + return simatic_ipc_batt_remove(pdev, NULL); 234 + } 235 + 236 + static int simatic_ipc_batt_io_probe(struct platform_device *pdev) 237 + { 238 + return simatic_ipc_batt_probe(pdev, NULL); 239 + } 240 + 241 + static struct platform_driver simatic_ipc_batt_driver = { 242 + .probe = simatic_ipc_batt_io_probe, 243 + .remove = simatic_ipc_batt_io_remove, 244 + .driver = { 245 + .name = KBUILD_MODNAME, 246 + }, 247 + }; 248 + 249 + module_platform_driver(simatic_ipc_batt_driver); 250 + 251 + MODULE_LICENSE("GPL"); 252 + MODULE_ALIAS("platform:" KBUILD_MODNAME); 253 + MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
+20
drivers/platform/x86/siemens/simatic-ipc-batt.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Siemens SIMATIC IPC driver for CMOS battery monitoring 4 + * 5 + * Copyright (c) Siemens AG, 2023 6 + * 7 + * Author: 8 + * Henning Schild <henning.schild@siemens.com> 9 + */ 10 + 11 + #ifndef _SIMATIC_IPC_BATT_H 12 + #define _SIMATIC_IPC_BATT_H 13 + 14 + int simatic_ipc_batt_probe(struct platform_device *pdev, 15 + struct gpiod_lookup_table *table); 16 + 17 + int simatic_ipc_batt_remove(struct platform_device *pdev, 18 + struct gpiod_lookup_table *table); 19 + 20 + #endif /* _SIMATIC_IPC_BATT_H */
+236
drivers/platform/x86/siemens/simatic-ipc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Siemens SIMATIC IPC platform driver 4 + * 5 + * Copyright (c) Siemens AG, 2018-2023 6 + * 7 + * Authors: 8 + * Henning Schild <henning.schild@siemens.com> 9 + * Jan Kiszka <jan.kiszka@siemens.com> 10 + * Gerd Haeussler <gerd.haeussler.ext@siemens.com> 11 + */ 12 + 13 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 + 15 + #include <linux/dmi.h> 16 + #include <linux/kernel.h> 17 + #include <linux/module.h> 18 + #include <linux/platform_data/x86/simatic-ipc.h> 19 + #include <linux/platform_device.h> 20 + 21 + static struct platform_device *ipc_led_platform_device; 22 + static struct platform_device *ipc_wdt_platform_device; 23 + static struct platform_device *ipc_batt_platform_device; 24 + 25 + static const struct dmi_system_id simatic_ipc_whitelist[] = { 26 + { 27 + .matches = { 28 + DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), 29 + }, 30 + }, 31 + {} 32 + }; 33 + 34 + static struct simatic_ipc_platform platform_data; 35 + 36 + #define SIMATIC_IPC_MAX_EXTRA_MODULES 2 37 + 38 + static struct { 39 + u32 station_id; 40 + u8 led_mode; 41 + u8 wdt_mode; 42 + u8 batt_mode; 43 + char *extra_modules[SIMATIC_IPC_MAX_EXTRA_MODULES]; 44 + } device_modes[] = { 45 + {SIMATIC_IPC_IPC127E, 46 + SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_127E, 47 + { "emc1403", NULL }}, 48 + {SIMATIC_IPC_IPC227D, 49 + SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, 50 + { "emc1403", NULL }}, 51 + {SIMATIC_IPC_IPC227E, 52 + SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E, 53 + { "emc1403", NULL }}, 54 + {SIMATIC_IPC_IPC227G, 55 + SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, 56 + { "nct6775", "w83627hf_wdt" }}, 57 + {SIMATIC_IPC_IPC277G, 58 + SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G, 59 + { "nct6775", "w83627hf_wdt" }}, 60 + {SIMATIC_IPC_IPC277E, 61 + SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E, SIMATIC_IPC_DEVICE_227E, 62 + { "emc1403", NULL }}, 63 + {SIMATIC_IPC_IPC427D, 64 + SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, 65 + { "emc1403", NULL }}, 66 + {SIMATIC_IPC_IPC427E, 67 + SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, 68 + { "emc1403", NULL }}, 69 + {SIMATIC_IPC_IPC477E, 70 + SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE, 71 + { "emc1403", NULL }}, 72 + {SIMATIC_IPC_IPCBX_39A, 73 + SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A, 74 + { "nct6775", "w83627hf_wdt" }}, 75 + {SIMATIC_IPC_IPCPX_39A, 76 + SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_39A, 77 + { "nct6775", "w83627hf_wdt" }}, 78 + {SIMATIC_IPC_IPCBX_21A, 79 + SIMATIC_IPC_DEVICE_BX_21A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_21A, 80 + { "emc1403", NULL }}, 81 + {SIMATIC_IPC_IPCBX_56A, 82 + SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A, 83 + { "emc1403", "w83627hf_wdt" }}, 84 + {SIMATIC_IPC_IPCBX_59A, 85 + SIMATIC_IPC_DEVICE_BX_59A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_BX_59A, 86 + { "emc1403", "w83627hf_wdt" }}, 87 + }; 88 + 89 + static int register_platform_devices(u32 station_id) 90 + { 91 + u8 ledmode = SIMATIC_IPC_DEVICE_NONE; 92 + u8 wdtmode = SIMATIC_IPC_DEVICE_NONE; 93 + u8 battmode = SIMATIC_IPC_DEVICE_NONE; 94 + char *pdevname; 95 + int i; 96 + 97 + for (i = 0; i < ARRAY_SIZE(device_modes); i++) { 98 + if (device_modes[i].station_id == station_id) { 99 + ledmode = device_modes[i].led_mode; 100 + wdtmode = device_modes[i].wdt_mode; 101 + battmode = device_modes[i].batt_mode; 102 + break; 103 + } 104 + } 105 + 106 + if (battmode != SIMATIC_IPC_DEVICE_NONE) { 107 + pdevname = KBUILD_MODNAME "_batt"; 108 + if (battmode == SIMATIC_IPC_DEVICE_127E) 109 + pdevname = KBUILD_MODNAME "_batt_apollolake"; 110 + if (battmode == SIMATIC_IPC_DEVICE_BX_21A) 111 + pdevname = KBUILD_MODNAME "_batt_elkhartlake"; 112 + if (battmode == SIMATIC_IPC_DEVICE_227G || 113 + battmode == SIMATIC_IPC_DEVICE_BX_39A || 114 + battmode == SIMATIC_IPC_DEVICE_BX_59A) 115 + pdevname = KBUILD_MODNAME "_batt_f7188x"; 116 + platform_data.devmode = battmode; 117 + ipc_batt_platform_device = 118 + platform_device_register_data(NULL, pdevname, 119 + PLATFORM_DEVID_NONE, &platform_data, 120 + sizeof(struct simatic_ipc_platform)); 121 + if (IS_ERR(ipc_batt_platform_device)) 122 + return PTR_ERR(ipc_batt_platform_device); 123 + 124 + pr_debug("device=%s created\n", 125 + ipc_batt_platform_device->name); 126 + } 127 + 128 + if (ledmode != SIMATIC_IPC_DEVICE_NONE) { 129 + pdevname = KBUILD_MODNAME "_leds"; 130 + if (ledmode == SIMATIC_IPC_DEVICE_127E) 131 + pdevname = KBUILD_MODNAME "_leds_gpio_apollolake"; 132 + if (ledmode == SIMATIC_IPC_DEVICE_227G || ledmode == SIMATIC_IPC_DEVICE_BX_59A) 133 + pdevname = KBUILD_MODNAME "_leds_gpio_f7188x"; 134 + if (ledmode == SIMATIC_IPC_DEVICE_BX_21A) 135 + pdevname = KBUILD_MODNAME "_leds_gpio_elkhartlake"; 136 + platform_data.devmode = ledmode; 137 + ipc_led_platform_device = 138 + platform_device_register_data(NULL, 139 + pdevname, PLATFORM_DEVID_NONE, 140 + &platform_data, 141 + sizeof(struct simatic_ipc_platform)); 142 + if (IS_ERR(ipc_led_platform_device)) 143 + return PTR_ERR(ipc_led_platform_device); 144 + 145 + pr_debug("device=%s created\n", 146 + ipc_led_platform_device->name); 147 + } 148 + 149 + if (wdtmode != SIMATIC_IPC_DEVICE_NONE) { 150 + platform_data.devmode = wdtmode; 151 + ipc_wdt_platform_device = 152 + platform_device_register_data(NULL, 153 + KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE, 154 + &platform_data, 155 + sizeof(struct simatic_ipc_platform)); 156 + if (IS_ERR(ipc_wdt_platform_device)) 157 + return PTR_ERR(ipc_wdt_platform_device); 158 + 159 + pr_debug("device=%s created\n", 160 + ipc_wdt_platform_device->name); 161 + } 162 + 163 + if (ledmode == SIMATIC_IPC_DEVICE_NONE && 164 + wdtmode == SIMATIC_IPC_DEVICE_NONE && 165 + battmode == SIMATIC_IPC_DEVICE_NONE) { 166 + pr_warn("unsupported IPC detected, station id=%08x\n", 167 + station_id); 168 + return -EINVAL; 169 + } 170 + 171 + return 0; 172 + } 173 + 174 + static void request_additional_modules(u32 station_id) 175 + { 176 + char **extra_modules = NULL; 177 + int i; 178 + 179 + for (i = 0; i < ARRAY_SIZE(device_modes); i++) { 180 + if (device_modes[i].station_id == station_id) { 181 + extra_modules = device_modes[i].extra_modules; 182 + break; 183 + } 184 + } 185 + 186 + if (!extra_modules) 187 + return; 188 + 189 + for (i = 0; i < SIMATIC_IPC_MAX_EXTRA_MODULES; i++) { 190 + if (extra_modules[i]) 191 + request_module(extra_modules[i]); 192 + else 193 + break; 194 + } 195 + } 196 + 197 + static int __init simatic_ipc_init_module(void) 198 + { 199 + const struct dmi_system_id *match; 200 + u32 station_id; 201 + int err; 202 + 203 + match = dmi_first_match(simatic_ipc_whitelist); 204 + if (!match) 205 + return 0; 206 + 207 + err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id); 208 + 209 + if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) { 210 + pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM); 211 + return 0; 212 + } 213 + 214 + request_additional_modules(station_id); 215 + 216 + return register_platform_devices(station_id); 217 + } 218 + 219 + static void __exit simatic_ipc_exit_module(void) 220 + { 221 + platform_device_unregister(ipc_led_platform_device); 222 + ipc_led_platform_device = NULL; 223 + 224 + platform_device_unregister(ipc_wdt_platform_device); 225 + ipc_wdt_platform_device = NULL; 226 + 227 + platform_device_unregister(ipc_batt_platform_device); 228 + ipc_batt_platform_device = NULL; 229 + } 230 + 231 + module_init(simatic_ipc_init_module); 232 + module_exit(simatic_ipc_exit_module); 233 + 234 + MODULE_LICENSE("GPL v2"); 235 + MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>"); 236 + MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
-151
drivers/platform/x86/simatic-ipc.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - /* 3 - * Siemens SIMATIC IPC platform driver 4 - * 5 - * Copyright (c) Siemens AG, 2018-2021 6 - * 7 - * Authors: 8 - * Henning Schild <henning.schild@siemens.com> 9 - * Jan Kiszka <jan.kiszka@siemens.com> 10 - * Gerd Haeussler <gerd.haeussler.ext@siemens.com> 11 - */ 12 - 13 - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 - 15 - #include <linux/dmi.h> 16 - #include <linux/kernel.h> 17 - #include <linux/module.h> 18 - #include <linux/pci.h> 19 - #include <linux/platform_data/x86/simatic-ipc.h> 20 - #include <linux/platform_device.h> 21 - 22 - static struct platform_device *ipc_led_platform_device; 23 - static struct platform_device *ipc_wdt_platform_device; 24 - 25 - static const struct dmi_system_id simatic_ipc_whitelist[] = { 26 - { 27 - .matches = { 28 - DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), 29 - }, 30 - }, 31 - {} 32 - }; 33 - 34 - static struct simatic_ipc_platform platform_data; 35 - 36 - static struct { 37 - u32 station_id; 38 - u8 led_mode; 39 - u8 wdt_mode; 40 - } device_modes[] = { 41 - {SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE}, 42 - {SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE}, 43 - {SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E}, 44 - {SIMATIC_IPC_IPC227G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G}, 45 - {SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E}, 46 - {SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE}, 47 - {SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E}, 48 - {SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E}, 49 - {SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G}, 50 - {SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G}, 51 - }; 52 - 53 - static int register_platform_devices(u32 station_id) 54 - { 55 - u8 ledmode = SIMATIC_IPC_DEVICE_NONE; 56 - u8 wdtmode = SIMATIC_IPC_DEVICE_NONE; 57 - char *pdevname = KBUILD_MODNAME "_leds"; 58 - int i; 59 - 60 - platform_data.devmode = SIMATIC_IPC_DEVICE_NONE; 61 - 62 - for (i = 0; i < ARRAY_SIZE(device_modes); i++) { 63 - if (device_modes[i].station_id == station_id) { 64 - ledmode = device_modes[i].led_mode; 65 - wdtmode = device_modes[i].wdt_mode; 66 - break; 67 - } 68 - } 69 - 70 - if (ledmode != SIMATIC_IPC_DEVICE_NONE) { 71 - if (ledmode == SIMATIC_IPC_DEVICE_127E) 72 - pdevname = KBUILD_MODNAME "_leds_gpio_apollolake"; 73 - if (ledmode == SIMATIC_IPC_DEVICE_227G) 74 - pdevname = KBUILD_MODNAME "_leds_gpio_f7188x"; 75 - platform_data.devmode = ledmode; 76 - ipc_led_platform_device = 77 - platform_device_register_data(NULL, 78 - pdevname, PLATFORM_DEVID_NONE, 79 - &platform_data, 80 - sizeof(struct simatic_ipc_platform)); 81 - if (IS_ERR(ipc_led_platform_device)) 82 - return PTR_ERR(ipc_led_platform_device); 83 - 84 - pr_debug("device=%s created\n", 85 - ipc_led_platform_device->name); 86 - } 87 - 88 - if (wdtmode == SIMATIC_IPC_DEVICE_227G) { 89 - request_module("w83627hf_wdt"); 90 - return 0; 91 - } 92 - 93 - if (wdtmode != SIMATIC_IPC_DEVICE_NONE) { 94 - platform_data.devmode = wdtmode; 95 - ipc_wdt_platform_device = 96 - platform_device_register_data(NULL, 97 - KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE, 98 - &platform_data, 99 - sizeof(struct simatic_ipc_platform)); 100 - if (IS_ERR(ipc_wdt_platform_device)) 101 - return PTR_ERR(ipc_wdt_platform_device); 102 - 103 - pr_debug("device=%s created\n", 104 - ipc_wdt_platform_device->name); 105 - } 106 - 107 - if (ledmode == SIMATIC_IPC_DEVICE_NONE && 108 - wdtmode == SIMATIC_IPC_DEVICE_NONE) { 109 - pr_warn("unsupported IPC detected, station id=%08x\n", 110 - station_id); 111 - return -EINVAL; 112 - } 113 - 114 - return 0; 115 - } 116 - 117 - static int __init simatic_ipc_init_module(void) 118 - { 119 - const struct dmi_system_id *match; 120 - u32 station_id; 121 - int err; 122 - 123 - match = dmi_first_match(simatic_ipc_whitelist); 124 - if (!match) 125 - return 0; 126 - 127 - err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id); 128 - 129 - if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) { 130 - pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM); 131 - return 0; 132 - } 133 - 134 - return register_platform_devices(station_id); 135 - } 136 - 137 - static void __exit simatic_ipc_exit_module(void) 138 - { 139 - platform_device_unregister(ipc_led_platform_device); 140 - ipc_led_platform_device = NULL; 141 - 142 - platform_device_unregister(ipc_wdt_platform_device); 143 - ipc_wdt_platform_device = NULL; 144 - } 145 - 146 - module_init(simatic_ipc_init_module); 147 - module_exit(simatic_ipc_exit_module); 148 - 149 - MODULE_LICENSE("GPL v2"); 150 - MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>"); 151 - MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
+59 -15
drivers/platform/x86/system76_acpi.c
··· 2 2 /* 3 3 * System76 ACPI Driver 4 4 * 5 - * Copyright (C) 2019 System76 5 + * Copyright (C) 2023 System76 6 6 * 7 7 * This program is free software; you can redistribute it and/or modify 8 8 * it under the terms of the GNU General Public License version 2 as ··· 24 24 25 25 #include <acpi/battery.h> 26 26 27 + enum kbled_type { 28 + KBLED_NONE, 29 + KBLED_WHITE, 30 + KBLED_RGB, 31 + }; 32 + 27 33 struct system76_data { 28 34 struct acpi_device *acpi_dev; 29 35 struct led_classdev ap_led; ··· 42 36 union acpi_object *ntmp; 43 37 struct input_dev *input; 44 38 bool has_open_ec; 39 + enum kbled_type kbled_type; 45 40 }; 46 41 47 42 static const struct acpi_device_id device_ids[] = { ··· 334 327 335 328 data = container_of(led, struct system76_data, kb_led); 336 329 data->kb_brightness = value; 337 - return system76_set(data, "SKBL", (int)data->kb_brightness); 330 + if (acpi_has_method(acpi_device_handle(data->acpi_dev), "GKBK")) { 331 + return system76_set(data, "SKBB", (int)data->kb_brightness); 332 + } else { 333 + return system76_set(data, "SKBL", (int)data->kb_brightness); 334 + } 338 335 } 339 336 340 337 // Get the last set keyboard LED color ··· 410 399 { 411 400 int value; 412 401 413 - value = system76_get(data, "GKBL"); 402 + if (acpi_has_method(acpi_device_handle(data->acpi_dev), "GKBK")) { 403 + value = system76_get(data, "GKBB"); 404 + } else { 405 + value = system76_get(data, "GKBL"); 406 + } 407 + 414 408 if (value < 0) 415 409 return; 416 410 data->kb_brightness = value; ··· 475 459 { 476 460 int i; 477 461 478 - if (data->kb_color < 0) 462 + if (data->kbled_type != KBLED_RGB) 479 463 return; 464 + 480 465 if (data->kb_brightness > 0) { 481 466 for (i = 0; i < ARRAY_SIZE(kb_colors); i++) { 482 467 if (kb_colors[i] == data->kb_color) ··· 704 687 data->kb_led.flags = LED_BRIGHT_HW_CHANGED | LED_CORE_SUSPENDRESUME; 705 688 data->kb_led.brightness_get = kb_led_get; 706 689 data->kb_led.brightness_set_blocking = kb_led_set; 707 - if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) { 708 - data->kb_led.max_brightness = 255; 709 - data->kb_led.groups = system76_kb_led_color_groups; 710 - data->kb_toggle_brightness = 72; 711 - data->kb_color = 0xffffff; 712 - system76_set(data, "SKBC", data->kb_color); 690 + if (acpi_has_method(acpi_device_handle(data->acpi_dev), "GKBK")) { 691 + // Use the new ACPI methods 692 + data->kbled_type = system76_get(data, "GKBK"); 693 + 694 + switch (data->kbled_type) { 695 + case KBLED_NONE: 696 + // Nothing to do: Device will not be registered. 697 + break; 698 + case KBLED_WHITE: 699 + data->kb_led.max_brightness = 255; 700 + data->kb_toggle_brightness = 72; 701 + break; 702 + case KBLED_RGB: 703 + data->kb_led.max_brightness = 255; 704 + data->kb_led.groups = system76_kb_led_color_groups; 705 + data->kb_toggle_brightness = 72; 706 + data->kb_color = 0xffffff; 707 + system76_set(data, "SKBC", data->kb_color); 708 + break; 709 + } 713 710 } else { 714 - data->kb_led.max_brightness = 5; 715 - data->kb_color = -1; 711 + // Use the old ACPI methods 712 + if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) { 713 + data->kbled_type = KBLED_RGB; 714 + data->kb_led.max_brightness = 255; 715 + data->kb_led.groups = system76_kb_led_color_groups; 716 + data->kb_toggle_brightness = 72; 717 + data->kb_color = 0xffffff; 718 + system76_set(data, "SKBC", data->kb_color); 719 + } else { 720 + data->kbled_type = KBLED_WHITE; 721 + data->kb_led.max_brightness = 5; 722 + } 716 723 } 717 - err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led); 718 - if (err) 719 - return err; 724 + 725 + if (data->kbled_type != KBLED_NONE) { 726 + err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led); 727 + if (err) 728 + return err; 729 + } 720 730 721 731 data->input = devm_input_allocate_device(&acpi_dev->dev); 722 732 if (!data->input)
+31 -27
drivers/platform/x86/thinkpad_acpi.c
··· 50 50 #include <linux/kthread.h> 51 51 #include <linux/leds.h> 52 52 #include <linux/list.h> 53 + #include <linux/lockdep.h> 53 54 #include <linux/module.h> 54 55 #include <linux/mutex.h> 55 56 #include <linux/nvram.h> ··· 908 907 if (count > PAGE_SIZE - 1) 909 908 return -EINVAL; 910 909 911 - kernbuf = kmalloc(count + 1, GFP_KERNEL); 912 - if (!kernbuf) 913 - return -ENOMEM; 914 - 915 - if (copy_from_user(kernbuf, userbuf, count)) { 916 - kfree(kernbuf); 917 - return -EFAULT; 918 - } 919 - 920 - kernbuf[count] = 0; 910 + kernbuf = memdup_user_nul(userbuf, count); 911 + if (IS_ERR(kernbuf)) 912 + return PTR_ERR(kernbuf); 921 913 ret = ibm->write(kernbuf); 922 914 if (ret == 0) 923 915 ret = count; ··· 2060 2066 * hotkey_acpi_mask accordingly. Also resets any bits 2061 2067 * from hotkey_user_mask that are unavailable to be 2062 2068 * delivered (shadow requirement of the userspace ABI). 2063 - * 2064 - * Call with hotkey_mutex held 2065 2069 */ 2066 2070 static int hotkey_mask_get(void) 2067 2071 { 2072 + lockdep_assert_held(&hotkey_mutex); 2073 + 2068 2074 if (tp_features.hotkey_mask) { 2069 2075 u32 m = 0; 2070 2076 ··· 2100 2106 * Also calls hotkey_mask_get to update hotkey_acpi_mask. 2101 2107 * 2102 2108 * NOTE: does not set bits in hotkey_user_mask, but may reset them. 2103 - * 2104 - * Call with hotkey_mutex held 2105 2109 */ 2106 2110 static int hotkey_mask_set(u32 mask) 2107 2111 { ··· 2107 2115 int rc = 0; 2108 2116 2109 2117 const u32 fwmask = mask & ~hotkey_source_mask; 2118 + 2119 + lockdep_assert_held(&hotkey_mutex); 2110 2120 2111 2121 if (tp_features.hotkey_mask) { 2112 2122 for (i = 0; i < 32; i++) { ··· 2141 2147 2142 2148 /* 2143 2149 * Sets hotkey_user_mask and tries to set the firmware mask 2144 - * 2145 - * Call with hotkey_mutex held 2146 2150 */ 2147 2151 static int hotkey_user_mask_set(const u32 mask) 2148 2152 { 2149 2153 int rc; 2154 + 2155 + lockdep_assert_held(&hotkey_mutex); 2150 2156 2151 2157 /* Give people a chance to notice they are doing something that 2152 2158 * is bound to go boom on their users sooner or later */ ··· 2508 2514 return 0; 2509 2515 } 2510 2516 2511 - /* call with hotkey_mutex held */ 2512 2517 static void hotkey_poll_stop_sync(void) 2513 2518 { 2519 + lockdep_assert_held(&hotkey_mutex); 2520 + 2514 2521 if (tpacpi_hotkey_task) { 2515 2522 kthread_stop(tpacpi_hotkey_task); 2516 2523 tpacpi_hotkey_task = NULL; 2517 2524 } 2518 2525 } 2519 2526 2520 - /* call with hotkey_mutex held */ 2521 2527 static void hotkey_poll_setup(const bool may_warn) 2522 2528 { 2523 2529 const u32 poll_driver_mask = hotkey_driver_mask & hotkey_source_mask; 2524 2530 const u32 poll_user_mask = hotkey_user_mask & hotkey_source_mask; 2531 + 2532 + lockdep_assert_held(&hotkey_mutex); 2525 2533 2526 2534 if (hotkey_poll_freq > 0 && 2527 2535 (poll_driver_mask || ··· 2553 2557 mutex_unlock(&hotkey_mutex); 2554 2558 } 2555 2559 2556 - /* call with hotkey_mutex held */ 2557 2560 static void hotkey_poll_set_freq(unsigned int freq) 2558 2561 { 2562 + lockdep_assert_held(&hotkey_mutex); 2563 + 2559 2564 if (!freq) 2560 2565 hotkey_poll_stop_sync(); 2561 2566 ··· 3470 3473 if (tp_features.hotkey_mask) { 3471 3474 /* hotkey_source_mask *must* be zero for 3472 3475 * the first hotkey_mask_get to return hotkey_orig_mask */ 3476 + mutex_lock(&hotkey_mutex); 3473 3477 res = hotkey_mask_get(); 3478 + mutex_unlock(&hotkey_mutex); 3474 3479 if (res) 3475 3480 return res; 3476 3481 ··· 3571 3572 hotkey_exit(); 3572 3573 return res; 3573 3574 } 3575 + mutex_lock(&hotkey_mutex); 3574 3576 res = hotkey_mask_set(((hotkey_all_mask & ~hotkey_reserved_mask) 3575 3577 | hotkey_driver_mask) 3576 3578 & ~hotkey_source_mask); 3579 + mutex_unlock(&hotkey_mutex); 3577 3580 if (res < 0 && res != -ENXIO) { 3578 3581 hotkey_exit(); 3579 3582 return res; ··· 6529 6528 6530 6529 static struct mutex brightness_mutex; 6531 6530 6532 - /* NVRAM brightness access, 6533 - * call with brightness_mutex held! */ 6531 + /* NVRAM brightness access */ 6534 6532 static unsigned int tpacpi_brightness_nvram_get(void) 6535 6533 { 6536 6534 u8 lnvram; 6535 + 6536 + lockdep_assert_held(&brightness_mutex); 6537 6537 6538 6538 lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) 6539 6539 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) ··· 6583 6581 } 6584 6582 6585 6583 6586 - /* call with brightness_mutex held! */ 6587 6584 static int tpacpi_brightness_get_raw(int *status) 6588 6585 { 6589 6586 u8 lec = 0; 6587 + 6588 + lockdep_assert_held(&brightness_mutex); 6590 6589 6591 6590 switch (brightness_mode) { 6592 6591 case TPACPI_BRGHT_MODE_UCMS_STEP: ··· 6604 6601 } 6605 6602 } 6606 6603 6607 - /* call with brightness_mutex held! */ 6608 6604 /* do NOT call with illegal backlight level value */ 6609 6605 static int tpacpi_brightness_set_ec(unsigned int value) 6610 6606 { 6611 6607 u8 lec = 0; 6608 + 6609 + lockdep_assert_held(&brightness_mutex); 6612 6610 6613 6611 if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec))) 6614 6612 return -EIO; ··· 6622 6618 return 0; 6623 6619 } 6624 6620 6625 - /* call with brightness_mutex held! */ 6626 6621 static int tpacpi_brightness_set_ucmsstep(unsigned int value) 6627 6622 { 6628 6623 int cmos_cmd, inc; 6629 6624 unsigned int current_value, i; 6625 + 6626 + lockdep_assert_held(&brightness_mutex); 6630 6627 6631 6628 current_value = tpacpi_brightness_nvram_get(); 6632 6629 ··· 8077 8072 return true; 8078 8073 } 8079 8074 8080 - /* 8081 - * Call with fan_mutex held 8082 - */ 8083 8075 static void fan_update_desired_level(u8 status) 8084 8076 { 8077 + lockdep_assert_held(&fan_mutex); 8078 + 8085 8079 if ((status & 8086 8080 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { 8087 8081 if (status > 7)
+7 -19
drivers/platform/x86/wmi-bmof.c
··· 25 25 struct bin_attribute bmof_bin_attr; 26 26 }; 27 27 28 - static ssize_t 29 - read_bmof(struct file *filp, struct kobject *kobj, 30 - struct bin_attribute *attr, 31 - char *buf, loff_t off, size_t count) 28 + static ssize_t read_bmof(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, 29 + char *buf, loff_t off, size_t count) 32 30 { 33 - struct bmof_priv *priv = 34 - container_of(attr, struct bmof_priv, bmof_bin_attr); 31 + struct bmof_priv *priv = container_of(attr, struct bmof_priv, bmof_bin_attr); 35 32 36 - if (off < 0) 37 - return -EINVAL; 38 - 39 - if (off >= priv->bmofdata->buffer.length) 40 - return 0; 41 - 42 - if (count > priv->bmofdata->buffer.length - off) 43 - count = priv->bmofdata->buffer.length - off; 44 - 45 - memcpy(buf, priv->bmofdata->buffer.pointer + off, count); 46 - return count; 33 + return memory_read_from_buffer(buf, count, &off, priv->bmofdata->buffer.pointer, 34 + priv->bmofdata->buffer.length); 47 35 } 48 36 49 37 static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) ··· 63 75 priv->bmof_bin_attr.read = read_bmof; 64 76 priv->bmof_bin_attr.size = priv->bmofdata->buffer.length; 65 77 66 - ret = sysfs_create_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr); 78 + ret = device_create_bin_file(&wdev->dev, &priv->bmof_bin_attr); 67 79 if (ret) 68 80 goto err_free; 69 81 ··· 78 90 { 79 91 struct bmof_priv *priv = dev_get_drvdata(&wdev->dev); 80 92 81 - sysfs_remove_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr); 93 + device_remove_bin_file(&wdev->dev, &priv->bmof_bin_attr); 82 94 kfree(priv->bmofdata); 83 95 } 84 96
+3 -2
drivers/watchdog/Kconfig
··· 1680 1680 1681 1681 config SIEMENS_SIMATIC_IPC_WDT 1682 1682 tristate "Siemens Simatic IPC Watchdog" 1683 - depends on SIEMENS_SIMATIC_IPC 1683 + depends on SIEMENS_SIMATIC_IPC && PCI 1684 + default y 1684 1685 select WATCHDOG_CORE 1685 - select P2SB 1686 + select P2SB if X86 1686 1687 help 1687 1688 This driver adds support for several watchdogs found in Industrial 1688 1689 PCs from Siemens.
+6 -3
drivers/watchdog/simatic-ipc-wdt.c
··· 155 155 156 156 switch (plat->devmode) { 157 157 case SIMATIC_IPC_DEVICE_227E: 158 - if (!devm_request_region(dev, gp_status_reg_227e_res.start, 159 - resource_size(&gp_status_reg_227e_res), 160 - KBUILD_MODNAME)) { 158 + res = &gp_status_reg_227e_res; 159 + if (!request_muxed_region(res->start, resource_size(res), res->name)) { 161 160 dev_err(dev, 162 161 "Unable to register IO resource at %pR\n", 163 162 &gp_status_reg_227e_res); ··· 208 209 wdd_data.bootstatus = wd_setup(plat->devmode); 209 210 if (wdd_data.bootstatus) 210 211 dev_warn(dev, "last reboot caused by watchdog reset\n"); 212 + 213 + if (plat->devmode == SIMATIC_IPC_DEVICE_227E) 214 + release_region(gp_status_reg_227e_res.start, 215 + resource_size(&gp_status_reg_227e_res)); 211 216 212 217 watchdog_set_nowayout(&wdd_data, nowayout); 213 218 watchdog_stop_on_reboot(&wdd_data);
+2
include/linux/intel_tpmi.h
··· 27 27 struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index); 28 28 int tpmi_get_resource_count(struct auxiliary_device *auxdev); 29 29 30 + int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, int *locked, 31 + int *disabled); 30 32 #endif
+18 -1
include/linux/platform_data/x86/asus-wmi.h
··· 66 66 #define ASUS_WMI_DEVID_CAMERA 0x00060013 67 67 #define ASUS_WMI_DEVID_LID_FLIP 0x00060062 68 68 #define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077 69 + #define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E 69 70 70 71 /* Storage */ 71 72 #define ASUS_WMI_DEVID_CARDREADER 0x00080013 ··· 81 80 #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */ 82 81 #define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013 83 82 #define ASUS_WMI_DEVID_GPU_FAN_CTRL 0x00110014 83 + #define ASUS_WMI_DEVID_MID_FAN_CTRL 0x00110031 84 84 #define ASUS_WMI_DEVID_CPU_FAN_CURVE 0x00110024 85 85 #define ASUS_WMI_DEVID_GPU_FAN_CURVE 0x00110025 86 + #define ASUS_WMI_DEVID_MID_FAN_CURVE 0x00110032 87 + 88 + /* Tunables for AUS ROG laptops */ 89 + #define ASUS_WMI_DEVID_PPT_PL2_SPPT 0x001200A0 90 + #define ASUS_WMI_DEVID_PPT_PL1_SPL 0x001200A3 91 + #define ASUS_WMI_DEVID_PPT_APU_SPPT 0x001200B0 92 + #define ASUS_WMI_DEVID_PPT_PLAT_SPPT 0x001200B1 93 + #define ASUS_WMI_DEVID_PPT_FPPT 0x001200C1 94 + #define ASUS_WMI_DEVID_NV_DYN_BOOST 0x001200C0 95 + #define ASUS_WMI_DEVID_NV_THERM_TARGET 0x001200C2 86 96 87 97 /* Power */ 88 98 #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 ··· 107 95 /* Keyboard dock */ 108 96 #define ASUS_WMI_DEVID_KBD_DOCK 0x00120063 109 97 110 - /* dgpu on/off */ 98 + /* Charging mode - 1=Barrel, 2=USB */ 99 + #define ASUS_WMI_DEVID_CHARGE_MODE 0x0012006C 100 + 101 + /* epu is connected? 1 == true */ 102 + #define ASUS_WMI_DEVID_EGPU_CONNECTED 0x00090018 103 + /* egpu on/off */ 111 104 #define ASUS_WMI_DEVID_EGPU 0x00090019 112 105 113 106 /* dgpu on/off */
+4 -1
include/linux/platform_data/x86/simatic-ipc-base.h
··· 2 2 /* 3 3 * Siemens SIMATIC IPC drivers 4 4 * 5 - * Copyright (c) Siemens AG, 2018-2021 5 + * Copyright (c) Siemens AG, 2018-2023 6 6 * 7 7 * Authors: 8 8 * Henning Schild <henning.schild@siemens.com> ··· 20 20 #define SIMATIC_IPC_DEVICE_127E 3 21 21 #define SIMATIC_IPC_DEVICE_227E 4 22 22 #define SIMATIC_IPC_DEVICE_227G 5 23 + #define SIMATIC_IPC_DEVICE_BX_21A 6 24 + #define SIMATIC_IPC_DEVICE_BX_39A 7 25 + #define SIMATIC_IPC_DEVICE_BX_59A 8 23 26 24 27 struct simatic_ipc_platform { 25 28 u8 devmode;
+5 -1
include/linux/platform_data/x86/simatic-ipc.h
··· 2 2 /* 3 3 * Siemens SIMATIC IPC drivers 4 4 * 5 - * Copyright (c) Siemens AG, 2018-2021 5 + * Copyright (c) Siemens AG, 2018-2023 6 6 * 7 7 * Authors: 8 8 * Henning Schild <henning.schild@siemens.com> ··· 32 32 SIMATIC_IPC_IPC477E = 0x00000A02, 33 33 SIMATIC_IPC_IPC127E = 0x00000D01, 34 34 SIMATIC_IPC_IPC227G = 0x00000F01, 35 + SIMATIC_IPC_IPC277G = 0x00000F02, 35 36 SIMATIC_IPC_IPCBX_39A = 0x00001001, 36 37 SIMATIC_IPC_IPCPX_39A = 0x00001002, 38 + SIMATIC_IPC_IPCBX_21A = 0x00001101, 39 + SIMATIC_IPC_IPCBX_56A = 0x00001201, 40 + SIMATIC_IPC_IPCBX_59A = 0x00001202, 37 41 }; 38 42 39 43 static inline u32 simatic_ipc_get_station_id(u8 *data, int max_len)
+49 -2
tools/power/x86/intel-speed-select/isst-config.c
··· 5 5 */ 6 6 7 7 #include <linux/isst_if.h> 8 + #include <sys/utsname.h> 8 9 9 10 #include "isst.h" 10 11 ··· 16 15 int arg; 17 16 }; 18 17 19 - static const char *version_str = "v1.16"; 18 + static const char *version_str = "v1.17"; 20 19 21 20 static const int supported_api_ver = 2; 22 21 static struct isst_if_platform_info isst_platform_info; ··· 474 473 return online; 475 474 } 476 475 476 + static int get_kernel_version(int *major, int *minor) 477 + { 478 + struct utsname buf; 479 + int ret; 480 + 481 + ret = uname(&buf); 482 + if (ret) 483 + return ret; 484 + 485 + ret = sscanf(buf.release, "%d.%d", major, minor); 486 + if (ret != 2) 487 + return ret; 488 + 489 + return 0; 490 + } 491 + 492 + #define CPU0_HOTPLUG_DEPRECATE_MAJOR_VER 6 493 + #define CPU0_HOTPLUG_DEPRECATE_MINOR_VER 5 494 + 477 495 void set_cpu_online_offline(int cpu, int state) 478 496 { 479 497 char buffer[128]; 480 498 int fd, ret; 499 + 500 + if (!cpu) { 501 + int major, minor; 502 + 503 + ret = get_kernel_version(&major, &minor); 504 + if (!ret) { 505 + if (major > CPU0_HOTPLUG_DEPRECATE_MAJOR_VER || (major == CPU0_HOTPLUG_DEPRECATE_MAJOR_VER && 506 + minor >= CPU0_HOTPLUG_DEPRECATE_MINOR_VER)) { 507 + debug_printf("Ignore CPU 0 offline/online for kernel version >= %d.%d\n", major, minor); 508 + debug_printf("Use cgroups to isolate CPU 0\n"); 509 + return; 510 + } 511 + } 512 + } 481 513 482 514 snprintf(buffer, sizeof(buffer), 483 515 "/sys/devices/system/cpu/cpu%d/online", cpu); ··· 812 778 map.cpu_map[0].logical_cpu); 813 779 } else { 814 780 update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]); 781 + punit_id = cpu_map[i].punit_id; 815 782 } 816 783 } 817 784 cpu_map[i].initialized = 1; ··· 2656 2621 */ 2657 2622 void parse_cpu_command(char *optarg) 2658 2623 { 2659 - unsigned int start, end; 2624 + unsigned int start, end, invalid_count; 2660 2625 char *next; 2661 2626 2662 2627 next = optarg; 2628 + invalid_count = 0; 2663 2629 2664 2630 while (next && *next) { 2665 2631 if (*next == '-') /* no negative cpu numbers */ ··· 2670 2634 2671 2635 if (max_target_cpus < MAX_CPUS_IN_ONE_REQ) 2672 2636 target_cpus[max_target_cpus++] = start; 2637 + else 2638 + invalid_count = 1; 2673 2639 2674 2640 if (*next == '\0') 2675 2641 break; ··· 2698 2660 while (++start <= end) { 2699 2661 if (max_target_cpus < MAX_CPUS_IN_ONE_REQ) 2700 2662 target_cpus[max_target_cpus++] = start; 2663 + else 2664 + invalid_count = 1; 2701 2665 } 2702 2666 2703 2667 if (*next == ',') 2704 2668 next += 1; 2705 2669 else if (*next != '\0') 2706 2670 goto error; 2671 + } 2672 + 2673 + if (invalid_count) { 2674 + isst_ctdp_display_information_start(outf); 2675 + isst_display_error_info_message(1, "Too many CPUs in one request: max is", 1, MAX_CPUS_IN_ONE_REQ - 1); 2676 + isst_ctdp_display_information_end(outf); 2677 + exit(-1); 2707 2678 } 2708 2679 2709 2680 #ifdef DEBUG
+1 -1
tools/power/x86/intel-speed-select/isst-display.c
··· 442 442 } 443 443 444 444 if (ctdp_level->mem_freq) { 445 - snprintf(header, sizeof(header), "mem-frequency(MHz)"); 445 + snprintf(header, sizeof(header), "max-mem-frequency(MHz)"); 446 446 snprintf(value, sizeof(value), "%d", 447 447 ctdp_level->mem_freq); 448 448 format_and_print(outf, level + 2, header, value);
+1 -1
tools/power/x86/intel-speed-select/isst.h
··· 79 79 80 80 #define DISP_FREQ_MULTIPLIER 100 81 81 82 - #define MAX_PACKAGE_COUNT 8 82 + #define MAX_PACKAGE_COUNT 32 83 83 #define MAX_DIE_PER_PACKAGE 2 84 84 #define MAX_PUNIT_PER_DIE 8 85 85