Commit ea5f6ad9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'platform-drivers-x86-v6.10-1' of...

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

Pull x86 platform driver updates from Hans de Goede:

 - New drivers/platform/arm64 directory for arm64 embedded-controller
   drivers

 - New drivers:
    - Acer Aspire 1 embedded controllers (for arm64 models)
    - ACPI quickstart PNP0C32 buttons
    - Dell All-In-One backlight support (dell-uart-backlight)
    - Lenovo WMI camera buttons
    - Lenovo Yoga Tablet 2 Pro 1380F/L fast charging
    - MeeGoPad ANX7428 Type-C Cross Switch (power sequencing only)
    - MSI WMI sensors (fan speed sensors only for now)

 - Asus WMI:
    - 2024 ROG Mini-LED support
    - MCU powersave support
    - Vivobook GPU MUX support
    - Misc. other improvements

 - Ideapad laptop:
    - Export FnLock LED as LED class device
    - Switch platform profiles using thermal management key

 - Intel drivers:
    - IFS: various improvements
    - PMC: Lunar Lake support
    - SDSI: various improvements
    - TPMI/ISST: various improvements
    - tools: intel-speed-select: various improvements

 - MS Surface drivers:
    - Fan profile switching support
    - Surface Pro thermal sensors support

 - ThinkPad ACPI:
    - Reworked hotkey support to use sparse keymaps
    - Add support for new trackpoint-doubletap, Fn+N and Fn+G hotkeys

 - WMI core:
    - New WMI driver development guide

 - x86 Android tablets:
    - Lenovo Yoga Tablet 2 Pro 1380F/L support
    - Xiaomi MiPad 2 status LED and bezel touch buttons backlight
      support

 - Miscellaneous cleanups / fixes / improvements

* tag 'platform-drivers-x86-v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (128 commits)
  platform/x86: Add new MeeGoPad ANX7428 Type-C Cross Switch driver
  devm-helpers: Fix a misspelled cancellation in the comments
  tools arch x86: Add dell-uart-backlight-emulator
  platform/x86: Add new Dell UART backlight driver
  platform/x86: x86-android-tablets: Create LED device for Xiaomi Pad 2 bottom bezel touch buttons
  platform/x86: x86-android-tablets: Xiaomi pad2 RGB LED fwnode updates
  platform/x86: x86-android-tablets: Pass struct device to init()
  platform/x86/amd: pmc: Add new ACPI ID AMDI000B
  platform/x86/amd: pmf: Add new ACPI ID AMDI0105
  platform/x86: p2sb: Don't init until unassigned resources have been assigned
  platform/surface: aggregator: Log critical errors during SAM probing
  platform/x86: ISST: Support SST-BF and SST-TF per level
  platform/x86/fujitsu-laptop: Replace sprintf() with sysfs_emit()
  tools/power/x86/intel-speed-select: v1.19 release
  tools/power/x86/intel-speed-select: Display CPU as None for -1
  tools/power/x86/intel-speed-select: SST BF/TF support per level
  tools/power/x86/intel-speed-select: Increase number of CPUs displayed
  tools/power/x86/intel-speed-select: Present all TRL levels for turbo-freq
  tools/power/x86/intel-speed-select: Fix display for unsupported levels
  tools/power/x86/intel-speed-select: Support multiple dies
  ...
parents b426433c 2513563e
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
What:		/sys/kernel/debug/msi-wmi-platform-<wmi_device_name>/*
Date:		April 2024
KernelVersion:	6.10
Contact:	Armin Wolf <W_Armin@gmx.de>
Description:
		This file allows to execute the associated WMI method with the same name.

		To start the execution, write a  buffer containing the method arguments
		at file offset 0. Partial writes or writes at a different offset are not
		supported.

		The buffer returned by the WMI method can then be read from the file.

		See Documentation/wmi/devices/msi-wmi-platform.rst for details.
+26 −0
Original line number Diff line number Diff line
@@ -126,6 +126,14 @@ Description:
		Change the mini-LED mode:
			* 0 - Single-zone,
			* 1 - Multi-zone
			* 2 - Multi-zone strong (available on newer generation mini-led)

What:		/sys/devices/platform/<platform>/available_mini_led_mode
Date:		Apr 2024
KernelVersion:	6.10
Contact:	"Luke Jones" <luke@ljones.dev>
Description:
		List the available mini-led modes.

What:		/sys/devices/platform/<platform>/ppt_pl1_spl
Date:		Jun 2023
@@ -186,3 +194,21 @@ Contact: "Luke Jones" <luke@ljones.dev>
Description:
		Set the target temperature limit of the Nvidia dGPU:
			* min=75, max=87

What:		/sys/devices/platform/<platform>/boot_sound
Date:		Apr 2024
KernelVersion:	6.10
Contact:	"Luke Jones" <luke@ljones.dev>
Description:
		Set if the BIOS POST sound is played on boot.
			* 0 - False,
			* 1 - True

What:		/sys/devices/platform/<platform>/mcu_powersave
Date:		Apr 2024
KernelVersion:	6.10
Contact:	"Luke Jones" <luke@ljones.dev>
Description:
		Set if the MCU can go in to low-power mode on system sleep
			* 0 - False,
			* 1 - True
+60 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/platform/acer,aspire1-ec.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Acer Aspire 1 Embedded Controller

maintainers:
  - Nikita Travkin <nikita@trvn.ru>

description:
  The Acer Aspire 1 laptop uses an embedded controller to control battery
  and charging as well as to provide a set of misc features such as the
  laptop lid status and HPD events for the USB Type-C DP alt mode.

properties:
  compatible:
    const: acer,aspire1-ec

  reg:
    const: 0x76

  interrupts:
    maxItems: 1

  connector:
    $ref: /schemas/connector/usb-connector.yaml#

required:
  - compatible
  - reg
  - interrupts

additionalProperties: false

examples:
  - |
    #include <dt-bindings/interrupt-controller/irq.h>
    i2c {
        #address-cells = <1>;
        #size-cells = <0>;

        embedded-controller@76 {
            compatible = "acer,aspire1-ec";
            reg = <0x76>;

            interrupts-extended = <&tlmm 30 IRQ_TYPE_LEVEL_LOW>;

            connector {
                compatible = "usb-c-connector";

                port {
                    ec_dp_in: endpoint {
                        remote-endpoint = <&mdss_dp_out>;
                    };
                };
            };
        };
    };
+194 −0
Original line number Diff line number Diff line
.. SPDX-License-Identifier: GPL-2.0-or-later

===================================================
MSI WMI Platform Features driver (msi-wmi-platform)
===================================================

Introduction
============

Many MSI notebooks support various features like reading fan sensors. This features are controlled
by the embedded controller, with the ACPI firmware exposing a standard ACPI WMI interface on top
of the embedded controller interface.

WMI interface description
=========================

The WMI interface description can be decoded from the embedded binary MOF (bmof)
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:

::

  [WMI, Locale("MS\0x409"),
   Description("This class contains the definition of the package used in other classes"),
   guid("{ABBC0F60-8EA1-11d1-00A0-C90629100000}")]
  class Package {
    [WmiDataId(1), read, write, Description("16 bytes of data")] uint8 Bytes[16];
  };

  [WMI, Locale("MS\0x409"),
   Description("This class contains the definition of the package used in other classes"),
   guid("{ABBC0F63-8EA1-11d1-00A0-C90629100000}")]
  class Package_32 {
    [WmiDataId(1), read, write, Description("32 bytes of data")] uint8 Bytes[32];
  };

  [WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"),
   Description("Class used to operate methods on a package"),
   guid("{ABBC0F6E-8EA1-11d1-00A0-C90629100000}")]
  class MSI_ACPI {
    [key, read] string InstanceName;
    [read] boolean Active;

    [WmiMethodId(1), Implemented, read, write, Description("Return the contents of a package")]
    void GetPackage([out, id(0)] Package Data);

    [WmiMethodId(2), Implemented, read, write, Description("Set the contents of a package")]
    void SetPackage([in, id(0)] Package Data);

    [WmiMethodId(3), Implemented, read, write, Description("Return the contents of a package")]
    void Get_EC([out, id(0)] Package_32 Data);

    [WmiMethodId(4), Implemented, read, write, Description("Set the contents of a package")]
    void Set_EC([in, id(0)] Package_32 Data);

    [WmiMethodId(5), Implemented, read, write, Description("Return the contents of a package")]
    void Get_BIOS([in, out, id(0)] Package_32 Data);

    [WmiMethodId(6), Implemented, read, write, Description("Set the contents of a package")]
    void Set_BIOS([in, out, id(0)] Package_32 Data);

    [WmiMethodId(7), Implemented, read, write, Description("Return the contents of a package")]
    void Get_SMBUS([in, out, id(0)] Package_32 Data);

    [WmiMethodId(8), Implemented, read, write, Description("Set the contents of a package")]
    void Set_SMBUS([in, out, id(0)] Package_32 Data);

    [WmiMethodId(9), Implemented, read, write, Description("Return the contents of a package")]
    void Get_MasterBattery([in, out, id(0)] Package_32 Data);

    [WmiMethodId(10), Implemented, read, write, Description("Set the contents of a package")]
    void Set_MasterBattery([in, out, id(0)] Package_32 Data);

    [WmiMethodId(11), Implemented, read, write, Description("Return the contents of a package")]
    void Get_SlaveBattery([in, out, id(0)] Package_32 Data);

    [WmiMethodId(12), Implemented, read, write, Description("Set the contents of a package")]
    void Set_SlaveBattery([in, out, id(0)] Package_32 Data);

    [WmiMethodId(13), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Temperature([in, out, id(0)] Package_32 Data);

    [WmiMethodId(14), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Temperature([in, out, id(0)] Package_32 Data);

    [WmiMethodId(15), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Thermal([in, out, id(0)] Package_32 Data);

    [WmiMethodId(16), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Thermal([in, out, id(0)] Package_32 Data);

    [WmiMethodId(17), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Fan([in, out, id(0)] Package_32 Data);

    [WmiMethodId(18), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Fan([in, out, id(0)] Package_32 Data);

    [WmiMethodId(19), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Device([in, out, id(0)] Package_32 Data);

    [WmiMethodId(20), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Device([in, out, id(0)] Package_32 Data);

    [WmiMethodId(21), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Power([in, out, id(0)] Package_32 Data);

    [WmiMethodId(22), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Power([in, out, id(0)] Package_32 Data);

    [WmiMethodId(23), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Debug([in, out, id(0)] Package_32 Data);

    [WmiMethodId(24), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Debug([in, out, id(0)] Package_32 Data);

    [WmiMethodId(25), Implemented, read, write, Description("Return the contents of a package")]
    void Get_AP([in, out, id(0)] Package_32 Data);

    [WmiMethodId(26), Implemented, read, write, Description("Set the contents of a package")]
    void Set_AP([in, out, id(0)] Package_32 Data);

    [WmiMethodId(27), Implemented, read, write, Description("Return the contents of a package")]
    void Get_Data([in, out, id(0)] Package_32 Data);

    [WmiMethodId(28), Implemented, read, write, Description("Set the contents of a package")]
    void Set_Data([in, out, id(0)] Package_32 Data);

    [WmiMethodId(29), Implemented, read, write, Description("Return the contents of a package")]
    void Get_WMI([out, id(0)] Package_32 Data);
  };

Due to a peculiarity in how Windows handles the ``CreateByteField()`` ACPI operator (errors only
happen when a invalid byte field is ultimately accessed), all methods require a 32 byte input
buffer, even if the Binay MOF says otherwise.

The input buffer contains a single byte to select the subfeature to be accessed and 31 bytes of
input data, the meaning of which depends on the subfeature being accessed.

The output buffer contains a singe byte which signals success or failure (``0x00`` on failure)
and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.

WMI method Get_EC()
-------------------

Returns embedded controller information, the selected subfeature does not matter. The output
data contains a flag byte and a 28 byte controller firmware version string.

The first 4 bits of the flag byte contain the minor version of the embedded controller interface,
with the next 2 bits containing the major version of the embedded controller interface.

The 7th bit signals if the embedded controller page chaged (exact meaning is unknown), and the
last bit signals if the platform is a Tigerlake platform.

The MSI software seems to only use this interface when the last bit is set.

WMI method Get_Fan()
--------------------

Fan speed sensors can be accessed by selecting subfeature ``0x00``. The output data contains
up to four 16-bit fan speed readings in big-endian format. Most machines do not support all
four fan speed sensors, so the remaining reading are hardcoded to ``0x0000``.

The fan RPM readings can be calculated with the following formula:

        RPM = 480000 / <fan speed reading>

If the fan speed reading is zero, then the fan RPM is zero too.

WMI method Get_WMI()
--------------------

Returns the version of the ACPI WMI interface, the selected subfeature does not matter.
The output data contains two bytes, the first one contains the major version and the last one
contains the minor revision of the ACPI WMI interface.

The MSI software seems to only use this interface when the major version is greater than two.

Reverse-Engineering the MSI WMI Platform interface
==================================================

.. warning:: Randomly poking the embedded controller interface can potentially cause damage
             to the machine and other unwanted side effects, please be careful.

The underlying embedded controller interface is used by the ``msi-ec`` driver, and it seems
that many methods just copy a part of the embedded controller memory into the output buffer.

This means that the remaining WMI methods can be reverse-engineered by looking which part of
the embedded controller memory is accessed by the ACPI AML code. The driver also supports a
debugfs interface for directly executing WMI methods. Additionally, any safety checks regarding
unsupported hardware can be disabled by loading the module with ``force=true``.

More information about the MSI embedded controller interface can be found at the
`msi-ec project <https://github.com/BeardOverflow/msi-ec>`_.

Special thanks go to github user `glpnk` for showing how to decode the fan speed readings.
+178 −0
Original line number Diff line number Diff line
.. SPDX-License-Identifier: GPL-2.0-or-later

============================
WMI driver development guide
============================

The WMI subsystem provides a rich driver API for implementing WMI drivers,
documented at Documentation/driver-api/wmi.rst. This document will serve
as an introductory guide for WMI driver writers using this API. It is supposed
to be a successor to the original LWN article [1]_ which deals with WMI drivers
using the deprecated GUID-based WMI interface.

Obtaining WMI device information
--------------------------------

Before developing an WMI driver, information about the WMI device in question
must be obtained. The `lswmi <https://pypi.org/project/lswmi>`_ utility can be
used to extract detailed WMI device information using the following command:

::

  lswmi -V

The resulting output will contain information about all WMI devices available on
a given machine, plus some extra information.

In order to find out more about the interface used to communicate with a WMI device,
the `bmfdec <https://github.com/pali/bmfdec>`_ utilities can be used to decode
the Binary MOF (Managed Object Format) information used to describe WMI devices.
The ``wmi-bmof`` driver exposes this information to userspace, see
Documentation/wmi/devices/wmi-bmof.rst.

In order to retrieve the decoded Binary MOF information, use the following command (requires root):

::

  ./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof

Sometimes, looking at the disassembled ACPI tables used to describe the WMI device
helps in understanding how the WMI device is supposed to work. The path of the ACPI
method associated with a given WMI device can be retrieved using the ``lswmi`` utility
as mentioned above.

Basic WMI driver structure
--------------------------

The basic WMI driver is build around the struct wmi_driver, which is then bound
to matching WMI devices using a struct wmi_device_id table:

::

  static const struct wmi_device_id foo_id_table[] = {
         { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
         { }
  };
  MODULE_DEVICE_TABLE(wmi, foo_id_table);

  static struct wmi_driver foo_driver = {
        .driver = {
                .name = "foo",
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,        /* recommended */
                .pm = pm_sleep_ptr(&foo_dev_pm_ops),            /* optional */
        },
        .id_table = foo_id_table,
        .probe = foo_probe,
        .remove = foo_remove,         /* optional, devres is preferred */
        .notify = foo_notify,         /* optional, for event handling */
        .no_notify_data = true,       /* optional, enables events containing no additional data */
        .no_singleton = true,         /* required for new WMI drivers */
  };
  module_wmi_driver(foo_driver);

The probe() callback is called when the WMI driver is bound to a matching WMI device. Allocating
driver-specific data structures and initialising interfaces to other kernel subsystems should
normally be done in this function.

The remove() callback is then called when the WMI driver is unbound from a WMI device. In order
to unregister interfaces to other kernel subsystems and release resources, devres should be used.
This simplifies error handling during probe and often allows to omit this callback entirely, see
Documentation/driver-api/driver-model/devres.rst for details.

Please note that new WMI drivers are required to be able to be instantiated multiple times,
and are forbidden from using any deprecated GUID-based WMI functions. This means that the
WMI driver should be prepared for the scenario that multiple matching WMI devices are present
on a given machine.

Because of this, WMI drivers should use the state container design pattern as described in
Documentation/driver-api/driver-model/design-patterns.rst.

WMI method drivers
------------------

WMI drivers can call WMI device methods using wmidev_evaluate_method(), the
structure of the ACPI buffer passed to this function is device-specific and usually
needs some tinkering to get right. Looking at the ACPI tables containing the WMI
device usually helps here. The method id and instance number passed to this function
are also device-specific, looking at the decoded Binary MOF is usually enough to
find the right values.

The maximum instance number can be retrieved during runtime using wmidev_instance_count().

Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver.

WMI data block drivers
----------------------

WMI drivers can query WMI device data blocks using wmidev_block_query(), the
structure of the returned ACPI object is again device-specific. Some WMI devices
also allow for setting data blocks using wmidev_block_set().

The maximum instance number can also be retrieved using wmidev_instance_count().

Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example
WMI data block driver.

WMI event drivers
-----------------

WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver.
The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that
the structure of the ACPI object passed to this callback is device-specific, and freeing the
ACPI object is being done by the WMI subsystem, not the driver.

The WMI driver core will take care that the notify() callback will only be called after
the probe() callback has been called, and that no events are being received by the driver
right before and after calling its remove() callback.

However WMI driver developers should be aware that multiple WMI events can be received concurrently,
so any locking (if necessary) needs to be provided by the WMI driver itself.

In order to be able to receive WMI events containing no additional event data,
the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.

Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.

Handling multiple WMI devices at once
-------------------------------------

There are many cases of firmware vendors using multiple WMI devices to control different aspects
of a single physical device. This can make developing WMI drivers complicated, as those drivers
might need to communicate with each other to present a unified interface to userspace.

On such case involves a WMI event device which needs to talk to a WMI data block device or WMI
method device upon receiving an WMI event. In such a case, two WMI drivers should be developed,
one for the WMI event device and one for the other WMI device.

The WMI event device driver has only one purpose: to receive WMI events, validate any additional
event data and invoke a notifier chain. The other WMI driver adds itself to this notifier chain
during probing and thus gets notified every time a WMI event is received. This WMI driver might
then process the event further for example by using an input device.

For other WMI device constellations, similar mechanisms can be used.

Things to avoid
---------------

When developing WMI drivers, there are a couple of things which should be avoided:

- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs
- bypassing of the WMI subsystem when talking to WMI devices
- WMI drivers which cannot be instantiated multiple times.

Many older WMI drivers violate one or more points from this list. The reason for
this is that the WMI subsystem evolved significantly over the last two decades,
so there is a lot of legacy cruft inside older WMI drivers.

New WMI drivers are also required to conform to the linux kernel coding style as specified in
Documentation/process/coding-style.rst. The checkpatch utility can catch many common coding style
violations, you can invoke it with the following command:

::

  ./scripts/checkpatch.pl --strict <path to driver file>

References
==========

.. [1] https://lwn.net/Articles/391230/
Loading