platform-drivers-x86 for v5.13-1
Highlights: - Lots of Microsoft Surface work - platform-profile support for HP and Microsoft Surface devices - New WMI Gigabyte motherboard temperature monitoring driver - Intel PMC improvements for Tiger Lake and Alder Lake - Misc. bugfixes, improvements and quirk additions all over The following is an automated git shortlog grouped by driver: Add support for DYTC MMC_GET BIOS API.: - Add support for DYTC MMC_GET BIOS API. Adjust Dell drivers to a personal email address: - Adjust Dell drivers to a personal email address Fix typo in Kconfig: - Fix typo in Kconfig ISST: - Account for increased timeout in some cases MAINTAINERS: - Add missing section for alienware-wmi driver - Adjust Dell drivers to email alias - update MELLANOX HARDWARE PLATFORM SUPPORT maintainers Merge tag 'ib-mfd-platform-x86-v5.13' into review-hans: - Merge tag 'ib-mfd-platform-x86-v5.13' into review-hans Merge tag 'irq-no-autoen-2021-03-25' into review-hans: - Merge tag 'irq-no-autoen-2021-03-25' into review-hans Typo fix in the file classmate-laptop.c: - Typo fix in the file classmate-laptop.c add Gigabyte WMI temperature driver: - add Gigabyte WMI temperature driver add support for Advantech software defined button: - add support for Advantech software defined button asus-laptop: - fix kobj_to_dev.cocci warnings asus-wmi: - Add param to turn fn-lock mode on by default dell-wmi-sysman: - Make init_bios_attributes() ACPI object parsing more robust - Cleanup create_attributes_level_sysfs_files() - Make sysman_init() return -ENODEV of the interfaces are not found - Cleanup sysman_init() error-exit handling - Fix release_attributes_data() getting called twice on init_bios_attributes() failure - Make it safe to call exit_foo_attributes() multiple times - Fix possible NULL pointer deref on exit - Fix crash caused by calling kset_unregister twice docs: - driver-api: Add Surface DTX driver documentation genirq: - Add IRQF_NO_AUTOEN for request_irq/nmi() gigabyte-wmi: - add support for B550M AORUS PRO-P - add X570 AORUS ELITE hp-wmi: - add platform profile support - rename "thermal policy" to "thermal profile" intel-hid: - Fix spurious wakeups caused by tablet-mode events during suspend - Support Lenovo ThinkPad X1 Tablet Gen 2 intel-vbtn: - Remove unused KEYMAP_LEN define - Stop reporting SW_DOCK events intel_chtdc_ti_pwrbtn: - Fix missing IRQF_ONESHOT as only threaded handler intel_pmc_core: - Uninitialized data in pmc_core_lpm_latch_mode_write() - add ACPI dependency - Fix "unsigned 'ret' is never less than zero" smatch warning - Add support for Alder Lake PCH-P - Add LTR registers for Tiger Lake - Add option to set/clear LPM mode - Add requirements file to debugfs - Get LPM requirements for Tiger Lake - Show LPM residency in microseconds - Handle sub-states generically - Remove global struct pmc_dev - Don't use global pmcdev in quirks - export platform global reset bits via etr3 sysfs file - Ignore GBE LTR on Tiger Lake platforms - Update Kconfig intel_pmt_class: - Initial resource to 0 intel_pmt_crashlog: - Fix incorrect macros mfd: - intel_pmt: Add support for DG1 - intel_pmt: Fix nuisance messages and handling of disabled capabilities panasonic-laptop: - remove redundant assignment of variable result platform: - x86: ACPI: Get rid of ACPICA message printing platform/mellanox: - mlxreg-hotplug: move to use request_irq by IRQF_NO_AUTOEN flag - Typo fix in the file mlxbf-bootctl.c platform/surface: - aggregator: fix a bit test - aggregator: move to use request_irq by IRQF_NO_AUTOEN flag - aggregator_registry: Give devices time to set up when connecting - clean up a variable in surface_dtx_read() - fix semicolon.cocci warnings - aggregator_registry: Add support for Surface Pro 7+ - aggregator_registry: Make symbol 'ssam_base_hub_group' static - dtx: Add support for native SSAM devices - Add DTX driver - aggregator: Make SSAM_DEFINE_SYNC_REQUEST_x define static functions - Add platform profile driver - aggregator_registry: Add HID subsystem devices - aggregator_registry: Add DTX device - aggregator_registry: Add platform profile device - aggregator_registry: Add battery subsystem devices - aggregator_registry: Add base device hub - Set up Surface Aggregator device registry pmc_atom: - Match all Beckhoff Automation baytrail boards with critclk_systems DMI table thinkpad_acpi: - Add labels to the first 2 temperature sensors - Correct thermal sensor allocation - Correct minor typo - sysfs interface to get wwan antenna type - Disable DYTC CQL mode around switching to balanced mode - Allow the FnLock LED to change state - check dytc version for lapmode sysfs - Handle keyboard cover attach/detach events tools/power/x86/intel-speed-select: - v1.9 release - Drop __DATE__ and __TIME__ macros - Add options to force online - Process mailbox read error for core-power - Increase string size touchscreen_dmi: - Add info for the Teclast Tbook 11 tablet - Handle device properties with software node API wmi: - Make remove callback return void -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEEuvA7XScYQRpenhd+kuxHeUQDJ9wFAmCGbVEUHGhkZWdvZWRl QHJlZGhhdC5jb20ACgkQkuxHeUQDJ9x4ywgAo51ExPQcLMlEDdfpN7oa0ErT+4AF lKqOHO/g3Am63NwlAVZElKAJq+AChfQzZ+Idy9E/IirFplmhuoKBBRQoB+U9SwYS zerwNDwAh1j1ZLlWDo0BSsiJLdGJH3j5BvScjo57+Vfa75J9EofIGXvNEjLNxb7j djLc4FawAfaqL6YerKXZPvYIfpIw2+26SyxDw2s6KlYyBkPIEneQvto0ObWR3vLc 1iFxLgfxL1fYX7dD9e/9H84kIQzs/wgTduXmnSn32BcFw3YOtWpnpwB0wJ8IIXM0 8Ta6jH2ZGTbgfKaHZf2O+UObj8tRXFzjpx4neh5vybRrBsYELzQIm+W+jQ== =fsK6 -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 Pull x86 platform driver updates freom Hans de Goede: - lots of Microsoft Surface work - platform-profile support for HP and Microsoft Surface devices - new WMI Gigabyte motherboard temperature monitoring driver - Intel PMC improvements for Tiger Lake and Alder Lake - misc bugfixes, improvements and quirk additions all over * tag 'platform-drivers-x86-v5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (87 commits) platform/x86: gigabyte-wmi: add support for B550M AORUS PRO-P platform/x86: intel_pmc_core: Uninitialized data in pmc_core_lpm_latch_mode_write() platform/x86: intel_pmc_core: add ACPI dependency platform/surface: aggregator: fix a bit test platform/x86: intel_pmc_core: Fix "unsigned 'ret' is never less than zero" smatch warning platform/x86: touchscreen_dmi: Add info for the Teclast Tbook 11 tablet platform/x86: intel_pmc_core: Add support for Alder Lake PCH-P platform/x86: intel_pmc_core: Add LTR registers for Tiger Lake platform/x86: intel_pmc_core: Add option to set/clear LPM mode platform/x86: intel_pmc_core: Add requirements file to debugfs platform/x86: intel_pmc_core: Get LPM requirements for Tiger Lake platform/x86: intel_pmc_core: Show LPM residency in microseconds platform/x86: intel_pmc_core: Handle sub-states generically platform/x86: intel_pmc_core: Remove global struct pmc_dev platform/x86: intel_pmc_core: Don't use global pmcdev in quirks platform/x86: intel_chtdc_ti_pwrbtn: Fix missing IRQF_ONESHOT as only threaded handler platform/x86: gigabyte-wmi: add X570 AORUS ELITE platform/x86: thinkpad_acpi: Add labels to the first 2 temperature sensors platform/x86: pmc_atom: Match all Beckhoff Automation baytrail boards with critclk_systems DMI table platform/x86: add Gigabyte WMI temperature driver ...
This commit is contained in:
commit
90035c28f1
20
Documentation/ABI/testing/sysfs-platform-intel-pmc
Normal file
20
Documentation/ABI/testing/sysfs-platform-intel-pmc
Normal file
@ -0,0 +1,20 @@
|
||||
What: /sys/devices/platform/<platform>/etr3
|
||||
Date: Apr 2021
|
||||
KernelVersion: 5.13
|
||||
Contact: "Tomas Winkler" <tomas.winkler@intel.com>
|
||||
Description:
|
||||
The file exposes "Extended Test Mode Register 3" global
|
||||
reset bits. The bits are used during an Intel platform
|
||||
manufacturing process to indicate that consequent reset
|
||||
of the platform is a "global reset". This type of reset
|
||||
is required in order for manufacturing configurations
|
||||
to take effect.
|
||||
|
||||
Display global reset setting bits for PMC.
|
||||
* bit 31 - global reset is locked
|
||||
* bit 20 - global reset is set
|
||||
Writing bit 20 value to the etr3 will induce
|
||||
a platform "global reset" upon consequent platform reset,
|
||||
in case the register is not locked.
|
||||
The "global reset bit" should be locked on a production
|
||||
system and the file is in read-only mode.
|
@ -52,6 +52,7 @@ detailed description):
|
||||
- LCD Shadow (PrivacyGuard) enable and disable
|
||||
- Lap mode sensor
|
||||
- Setting keyboard language
|
||||
- WWAN Antenna type
|
||||
|
||||
A compatibility table by model and feature is maintained on the web
|
||||
site, http://ibm-acpi.sf.net/. I appreciate any success or failure
|
||||
@ -1490,6 +1491,25 @@ fr(French), fr-ch(French(Switzerland)), hu(Hungarian), it(Italy), jp (Japan),
|
||||
nl(Dutch), nn(Norway), pl(Polish), pt(portugese), sl(Slovenian), sv(Sweden),
|
||||
tr(Turkey)
|
||||
|
||||
WWAN Antenna type
|
||||
-----------------
|
||||
|
||||
sysfs: wwan_antenna_type
|
||||
|
||||
On some newer Thinkpads we need to set SAR value based on the antenna
|
||||
type. This interface will be used by userspace to get the antenna type
|
||||
and set the corresponding SAR value, as is required for FCC certification.
|
||||
|
||||
The available commands are::
|
||||
|
||||
cat /sys/devices/platform/thinkpad_acpi/wwan_antenna_type
|
||||
|
||||
Currently 2 antenna types are supported as mentioned below:
|
||||
- type a
|
||||
- type b
|
||||
|
||||
The property is read-only. If the platform doesn't have support the sysfs
|
||||
class is not created.
|
||||
|
||||
Adaptive keyboard
|
||||
-----------------
|
||||
|
@ -248,7 +248,7 @@ This example defines a function
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
|
||||
static int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
|
||||
|
||||
executing the specified request, with the controller passed in when calling
|
||||
said function. In this example, the argument is provided via the ``arg``
|
||||
@ -296,7 +296,7 @@ This invocation of the macro defines a function
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
|
||||
static int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
|
||||
|
||||
executing the specified request, using the device IDs and controller given
|
||||
in the client device. The full list of such macros for client devices is:
|
||||
|
718
Documentation/driver-api/surface_aggregator/clients/dtx.rst
Normal file
718
Documentation/driver-api/surface_aggregator/clients/dtx.rst
Normal file
@ -0,0 +1,718 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
.. |__u16| replace:: :c:type:`__u16 <__u16>`
|
||||
.. |sdtx_event| replace:: :c:type:`struct sdtx_event <sdtx_event>`
|
||||
.. |sdtx_event_code| replace:: :c:type:`enum sdtx_event_code <sdtx_event_code>`
|
||||
.. |sdtx_base_info| replace:: :c:type:`struct sdtx_base_info <sdtx_base_info>`
|
||||
.. |sdtx_device_mode| replace:: :c:type:`struct sdtx_device_mode <sdtx_device_mode>`
|
||||
|
||||
======================================================
|
||||
User-Space DTX (Clipboard Detachment System) Interface
|
||||
======================================================
|
||||
|
||||
The ``surface_dtx`` driver is responsible for proper clipboard detachment
|
||||
and re-attachment handling. To this end, it provides the ``/dev/surface/dtx``
|
||||
device file, through which it can interface with a user-space daemon. This
|
||||
daemon is then ultimately responsible for determining and taking necessary
|
||||
actions, such as unmounting devices attached to the base,
|
||||
unloading/reloading the graphics-driver, user-notifications, etc.
|
||||
|
||||
There are two basic communication principles used in this driver: Commands
|
||||
(in other parts of the documentation also referred to as requests) and
|
||||
events. Commands are sent to the EC and may have a different implications in
|
||||
different contexts. Events are sent by the EC upon some internal state
|
||||
change. Commands are always driver-initiated, whereas events are always
|
||||
initiated by the EC.
|
||||
|
||||
.. contents::
|
||||
|
||||
Nomenclature
|
||||
============
|
||||
|
||||
* **Clipboard:**
|
||||
The detachable upper part of the Surface Book, housing the screen and CPU.
|
||||
|
||||
* **Base:**
|
||||
The lower part of the Surface Book from which the clipboard can be
|
||||
detached, optionally (model dependent) housing the discrete GPU (dGPU).
|
||||
|
||||
* **Latch:**
|
||||
The mechanism keeping the clipboard attached to the base in normal
|
||||
operation and allowing it to be detached when requested.
|
||||
|
||||
* **Silently ignored commands:**
|
||||
The command is accepted by the EC as a valid command and acknowledged
|
||||
(following the standard communication protocol), but the EC does not act
|
||||
upon it, i.e. ignores it.e upper part of the
|
||||
|
||||
|
||||
Detachment Process
|
||||
==================
|
||||
|
||||
Warning: This part of the documentation is based on reverse engineering and
|
||||
testing and thus may contain errors or be incomplete.
|
||||
|
||||
Latch States
|
||||
------------
|
||||
|
||||
The latch mechanism has two major states: *open* and *closed*. In the
|
||||
*closed* state (default), the clipboard is secured to the base, whereas in
|
||||
the *open* state, the clipboard can be removed by a user.
|
||||
|
||||
The latch can additionally be locked and, correspondingly, unlocked, which
|
||||
can influence the detachment procedure. Specifically, this locking mechanism
|
||||
is intended to prevent the dGPU, positioned in the base of the device, from
|
||||
being hot-unplugged while in use. More details can be found in the
|
||||
documentation for the detachment procedure below. By default, the latch is
|
||||
unlocked.
|
||||
|
||||
Detachment Procedure
|
||||
--------------------
|
||||
|
||||
Note that the detachment process is governed fully by the EC. The
|
||||
``surface_dtx`` driver only relays events from the EC to user-space and
|
||||
commands from user-space to the EC, i.e. it does not influence this process.
|
||||
|
||||
The detachment process is started with the user pressing the *detach* button
|
||||
on the base of the device or executing the ``SDTX_IOCTL_LATCH_REQUEST`` IOCTL.
|
||||
Following that:
|
||||
|
||||
1. The EC turns on the indicator led on the detach-button, sends a
|
||||
*detach-request* event (``SDTX_EVENT_REQUEST``), and awaits further
|
||||
instructions/commands. In case the latch is unlocked, the led will flash
|
||||
green. If the latch has been locked, the led will be solid red
|
||||
|
||||
2. The event is, via the ``surface_dtx`` driver, relayed to user-space, where
|
||||
an appropriate user-space daemon can handle it and send instructions back
|
||||
to the EC via IOCTLs provided by this driver.
|
||||
|
||||
3. The EC waits for instructions from user-space and acts according to them.
|
||||
If the EC does not receive any instructions in a given period, it will
|
||||
time out and continue as follows:
|
||||
|
||||
- If the latch is unlocked, the EC will open the latch and the clipboard
|
||||
can be detached from the base. This is the exact behavior as without
|
||||
this driver or any user-space daemon. See the ``SDTX_IOCTL_LATCH_CONFIRM``
|
||||
description below for more details on the follow-up behavior of the EC.
|
||||
|
||||
- If the latch is locked, the EC will *not* open the latch, meaning the
|
||||
clipboard cannot be detached from the base. Furthermore, the EC sends
|
||||
an cancel event (``SDTX_EVENT_CANCEL``) detailing this with the cancel
|
||||
reason ``SDTX_DETACH_TIMEDOUT`` (see :ref:`events` for details).
|
||||
|
||||
Valid responses by a user-space daemon to a detachment request event are:
|
||||
|
||||
- Execute ``SDTX_IOCTL_LATCH_REQUEST``. This will immediately abort the
|
||||
detachment process. Furthermore, the EC will send a detach-request event,
|
||||
similar to the user pressing the detach-button to cancel said process (see
|
||||
below).
|
||||
|
||||
- Execute ``SDTX_IOCTL_LATCH_CONFIRM``. This will cause the EC to open the
|
||||
latch, after which the user can separate clipboard and base.
|
||||
|
||||
As this changes the latch state, a *latch-status* event
|
||||
(``SDTX_EVENT_LATCH_STATUS``) will be sent once the latch has been opened
|
||||
successfully. If the EC fails to open the latch, e.g. due to hardware
|
||||
error or low battery, a latch-cancel event (``SDTX_EVENT_CANCEL``) will be
|
||||
sent with the cancel reason indicating the specific failure.
|
||||
|
||||
If the latch is currently locked, the latch will automatically be
|
||||
unlocked before it is opened.
|
||||
|
||||
- Execute ``SDTX_IOCTL_LATCH_HEARTBEAT``. This will reset the internal timeout.
|
||||
No other actions will be performed, i.e. the detachment process will neither
|
||||
be completed nor canceled, and the EC will still be waiting for further
|
||||
responses.
|
||||
|
||||
- Execute ``SDTX_IOCTL_LATCH_CANCEL``. This will abort the detachment process,
|
||||
similar to ``SDTX_IOCTL_LATCH_REQUEST``, described above, or the button
|
||||
press, described below. A *generic request* event (``SDTX_EVENT_REQUEST``)
|
||||
is send in response to this. In contrast to those, however, this command
|
||||
does not trigger a new detachment process if none is currently in
|
||||
progress.
|
||||
|
||||
- Do nothing. The detachment process eventually times out as described in
|
||||
point 3.
|
||||
|
||||
See :ref:`ioctls` for more details on these responses.
|
||||
|
||||
It is important to note that, if the user presses the detach button at any
|
||||
point when a detachment operation is in progress (i.e. after the EC has sent
|
||||
the initial *detach-request* event (``SDTX_EVENT_REQUEST``) and before it
|
||||
received the corresponding response concluding the process), the detachment
|
||||
process is canceled on the EC-level and an identical event is being sent.
|
||||
Thus a *detach-request* event, by itself, does not signal the start of the
|
||||
detachment process.
|
||||
|
||||
The detachment process may further be canceled by the EC due to hardware
|
||||
failures or a low clipboard battery. This is done via a cancel event
|
||||
(``SDTX_EVENT_CANCEL``) with the corresponding cancel reason.
|
||||
|
||||
|
||||
User-Space Interface Documentation
|
||||
==================================
|
||||
|
||||
Error Codes and Status Values
|
||||
-----------------------------
|
||||
|
||||
Error and status codes are divided into different categories, which can be
|
||||
used to determine if the status code is an error, and, if it is, the
|
||||
severity and type of that error. The current categories are:
|
||||
|
||||
.. flat-table:: Overview of Status/Error Categories.
|
||||
:widths: 2 1 3
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Value
|
||||
- Short Description
|
||||
|
||||
* - ``STATUS``
|
||||
- ``0x0000``
|
||||
- Non-error status codes.
|
||||
|
||||
* - ``RUNTIME_ERROR``
|
||||
- ``0x1000``
|
||||
- Non-critical runtime errors.
|
||||
|
||||
* - ``HARDWARE_ERROR``
|
||||
- ``0x2000``
|
||||
- Critical hardware failures.
|
||||
|
||||
* - ``UNKNOWN``
|
||||
- ``0xF000``
|
||||
- Unknown error codes.
|
||||
|
||||
Other categories are reserved for future use. The ``SDTX_CATEGORY()`` macro
|
||||
can be used to determine the category of any status value. The
|
||||
``SDTX_SUCCESS()`` macro can be used to check if the status value is a
|
||||
success value (``SDTX_CATEGORY_STATUS``) or if it indicates a failure.
|
||||
|
||||
Unknown status or error codes sent by the EC are assigned to the ``UNKNOWN``
|
||||
category by the driver and may be implemented via their own code in the
|
||||
future.
|
||||
|
||||
Currently used error codes are:
|
||||
|
||||
.. flat-table:: Overview of Error Codes.
|
||||
:widths: 2 1 1 3
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Category
|
||||
- Value
|
||||
- Short Description
|
||||
|
||||
* - ``SDTX_DETACH_NOT_FEASIBLE``
|
||||
- ``RUNTIME``
|
||||
- ``0x1001``
|
||||
- Detachment not feasible due to low clipboard battery.
|
||||
|
||||
* - ``SDTX_DETACH_TIMEDOUT``
|
||||
- ``RUNTIME``
|
||||
- ``0x1002``
|
||||
- Detachment process timed out while the latch was locked.
|
||||
|
||||
* - ``SDTX_ERR_FAILED_TO_OPEN``
|
||||
- ``HARDWARE``
|
||||
- ``0x2001``
|
||||
- Failed to open latch.
|
||||
|
||||
* - ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``
|
||||
- ``HARDWARE``
|
||||
- ``0x2002``
|
||||
- Failed to keep latch open.
|
||||
|
||||
* - ``SDTX_ERR_FAILED_TO_CLOSE``
|
||||
- ``HARDWARE``
|
||||
- ``0x2003``
|
||||
- Failed to close latch.
|
||||
|
||||
Other error codes are reserved for future use. Non-error status codes may
|
||||
overlap and are generally only unique within their use-case:
|
||||
|
||||
.. flat-table:: Latch Status Codes.
|
||||
:widths: 2 1 1 3
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Category
|
||||
- Value
|
||||
- Short Description
|
||||
|
||||
* - ``SDTX_LATCH_CLOSED``
|
||||
- ``STATUS``
|
||||
- ``0x0000``
|
||||
- Latch is closed/has been closed.
|
||||
|
||||
* - ``SDTX_LATCH_OPENED``
|
||||
- ``STATUS``
|
||||
- ``0x0001``
|
||||
- Latch is open/has been opened.
|
||||
|
||||
.. flat-table:: Base State Codes.
|
||||
:widths: 2 1 1 3
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Category
|
||||
- Value
|
||||
- Short Description
|
||||
|
||||
* - ``SDTX_BASE_DETACHED``
|
||||
- ``STATUS``
|
||||
- ``0x0000``
|
||||
- Base has been detached/is not present.
|
||||
|
||||
* - ``SDTX_BASE_ATTACHED``
|
||||
- ``STATUS``
|
||||
- ``0x0001``
|
||||
- Base has been attached/is present.
|
||||
|
||||
Again, other codes are reserved for future use.
|
||||
|
||||
.. _events:
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
Events can be received by reading from the device file. They are disabled by
|
||||
default and have to be enabled by executing ``SDTX_IOCTL_EVENTS_ENABLE``
|
||||
first. All events follow the layout prescribed by |sdtx_event|. Specific
|
||||
event types can be identified by their event code, described in
|
||||
|sdtx_event_code|. Note that other event codes are reserved for future use,
|
||||
thus an event parser must be able to handle any unknown/unsupported event
|
||||
types gracefully, by relying on the payload length given in the event header.
|
||||
|
||||
Currently provided event types are:
|
||||
|
||||
.. flat-table:: Overview of DTX events.
|
||||
:widths: 2 1 1 3
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Code
|
||||
- Payload
|
||||
- Short Description
|
||||
|
||||
* - ``SDTX_EVENT_REQUEST``
|
||||
- ``1``
|
||||
- ``0`` bytes
|
||||
- Detachment process initiated/aborted.
|
||||
|
||||
* - ``SDTX_EVENT_CANCEL``
|
||||
- ``2``
|
||||
- ``2`` bytes
|
||||
- EC canceled detachment process.
|
||||
|
||||
* - ``SDTX_EVENT_BASE_CONNECTION``
|
||||
- ``3``
|
||||
- ``4`` bytes
|
||||
- Base connection state changed.
|
||||
|
||||
* - ``SDTX_EVENT_LATCH_STATUS``
|
||||
- ``4``
|
||||
- ``2`` bytes
|
||||
- Latch status changed.
|
||||
|
||||
* - ``SDTX_EVENT_DEVICE_MODE``
|
||||
- ``5``
|
||||
- ``2`` bytes
|
||||
- Device mode changed.
|
||||
|
||||
Individual events in more detail:
|
||||
|
||||
``SDTX_EVENT_REQUEST``
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sent when a detachment process is started or, if in progress, aborted by the
|
||||
user, either via a detach button press or a detach request
|
||||
(``SDTX_IOCTL_LATCH_REQUEST``) being sent from user-space.
|
||||
|
||||
Does not have any payload.
|
||||
|
||||
``SDTX_EVENT_CANCEL``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sent when a detachment process is canceled by the EC due to unfulfilled
|
||||
preconditions (e.g. clipboard battery too low to detach) or hardware
|
||||
failure. The reason for cancellation is given in the event payload detailed
|
||||
below and can be one of
|
||||
|
||||
* ``SDTX_DETACH_TIMEDOUT``: Detachment timed out while the latch was locked.
|
||||
The latch has neither been opened nor unlocked.
|
||||
|
||||
* ``SDTX_DETACH_NOT_FEASIBLE``: Detachment not feasible due to low clipboard
|
||||
battery.
|
||||
|
||||
* ``SDTX_ERR_FAILED_TO_OPEN``: Could not open the latch (hardware failure).
|
||||
|
||||
* ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``: Could not keep the latch open (hardware
|
||||
failure).
|
||||
|
||||
* ``SDTX_ERR_FAILED_TO_CLOSE``: Could not close the latch (hardware failure).
|
||||
|
||||
Other error codes in this context are reserved for future use.
|
||||
|
||||
These codes can be classified via the ``SDTX_CATEGORY()`` macro to discern
|
||||
between critical hardware errors (``SDTX_CATEGORY_HARDWARE_ERROR``) or
|
||||
runtime errors (``SDTX_CATEGORY_RUNTIME_ERROR``), the latter of which may
|
||||
happen during normal operation if certain preconditions for detachment are
|
||||
not given.
|
||||
|
||||
.. flat-table:: Detachment Cancel Event Payload
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - ``reason``
|
||||
- |__u16|
|
||||
- Reason for cancellation.
|
||||
|
||||
``SDTX_EVENT_BASE_CONNECTION``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sent when the base connection state has changed, i.e. when the base has been
|
||||
attached, detached, or detachment has become infeasible due to low clipboard
|
||||
battery. The new state and, if a base is connected, ID of the base is
|
||||
provided as payload of type |sdtx_base_info| with its layout presented
|
||||
below:
|
||||
|
||||
.. flat-table:: Base-Connection-Change Event Payload
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - ``state``
|
||||
- |__u16|
|
||||
- Base connection state.
|
||||
|
||||
* - ``base_id``
|
||||
- |__u16|
|
||||
- Type of base connected (zero if none).
|
||||
|
||||
Possible values for ``state`` are:
|
||||
|
||||
* ``SDTX_BASE_DETACHED``,
|
||||
* ``SDTX_BASE_ATTACHED``, and
|
||||
* ``SDTX_DETACH_NOT_FEASIBLE``.
|
||||
|
||||
Other values are reserved for future use.
|
||||
|
||||
``SDTX_EVENT_LATCH_STATUS``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sent when the latch status has changed, i.e. when the latch has been opened,
|
||||
closed, or an error occurred. The current status is provided as payload:
|
||||
|
||||
.. flat-table:: Latch-Status-Change Event Payload
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - ``status``
|
||||
- |__u16|
|
||||
- Latch status.
|
||||
|
||||
Possible values for ``status`` are:
|
||||
|
||||
* ``SDTX_LATCH_CLOSED``,
|
||||
* ``SDTX_LATCH_OPENED``,
|
||||
* ``SDTX_ERR_FAILED_TO_OPEN``,
|
||||
* ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``, and
|
||||
* ``SDTX_ERR_FAILED_TO_CLOSE``.
|
||||
|
||||
Other values are reserved for future use.
|
||||
|
||||
``SDTX_EVENT_DEVICE_MODE``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Sent when the device mode has changed. The new device mode is provided as
|
||||
payload:
|
||||
|
||||
.. flat-table:: Device-Mode-Change Event Payload
|
||||
:widths: 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - ``mode``
|
||||
- |__u16|
|
||||
- Device operation mode.
|
||||
|
||||
Possible values for ``mode`` are:
|
||||
|
||||
* ``SDTX_DEVICE_MODE_TABLET``,
|
||||
* ``SDTX_DEVICE_MODE_LAPTOP``, and
|
||||
* ``SDTX_DEVICE_MODE_STUDIO``.
|
||||
|
||||
Other values are reserved for future use.
|
||||
|
||||
.. _ioctls:
|
||||
|
||||
IOCTLs
|
||||
------
|
||||
|
||||
The following IOCTLs are provided:
|
||||
|
||||
.. flat-table:: Overview of DTX IOCTLs
|
||||
:widths: 1 1 1 1 4
|
||||
:header-rows: 1
|
||||
|
||||
* - Type
|
||||
- Number
|
||||
- Direction
|
||||
- Name
|
||||
- Description
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x21``
|
||||
- ``-``
|
||||
- ``EVENTS_ENABLE``
|
||||
- Enable events for the current file descriptor.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x22``
|
||||
- ``-``
|
||||
- ``EVENTS_DISABLE``
|
||||
- Disable events for the current file descriptor.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x23``
|
||||
- ``-``
|
||||
- ``LATCH_LOCK``
|
||||
- Lock the latch.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x24``
|
||||
- ``-``
|
||||
- ``LATCH_UNLOCK``
|
||||
- Unlock the latch.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x25``
|
||||
- ``-``
|
||||
- ``LATCH_REQUEST``
|
||||
- Request clipboard detachment.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x26``
|
||||
- ``-``
|
||||
- ``LATCH_CONFIRM``
|
||||
- Confirm clipboard detachment request.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x27``
|
||||
- ``-``
|
||||
- ``LATCH_HEARTBEAT``
|
||||
- Send heartbeat signal to EC.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x28``
|
||||
- ``-``
|
||||
- ``LATCH_CANCEL``
|
||||
- Cancel detachment process.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x29``
|
||||
- ``R``
|
||||
- ``GET_BASE_INFO``
|
||||
- Get current base/connection information.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x2A``
|
||||
- ``R``
|
||||
- ``GET_DEVICE_MODE``
|
||||
- Get current device operation mode.
|
||||
|
||||
* - ``0xA5``
|
||||
- ``0x2B``
|
||||
- ``R``
|
||||
- ``GET_LATCH_STATUS``
|
||||
- Get current device latch status.
|
||||
|
||||
``SDTX_IOCTL_EVENTS_ENABLE``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x22)``.
|
||||
|
||||
Enable events for the current file descriptor. Events can be obtained by
|
||||
reading from the device, if enabled. Events are disabled by default.
|
||||
|
||||
``SDTX_IOCTL_EVENTS_DISABLE``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x22)``.
|
||||
|
||||
Disable events for the current file descriptor. Events can be obtained by
|
||||
reading from the device, if enabled. Events are disabled by default.
|
||||
|
||||
``SDTX_IOCTL_LATCH_LOCK``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x23)``.
|
||||
|
||||
Locks the latch, causing the detachment procedure to abort without opening
|
||||
the latch on timeout. The latch is unlocked by default. This command will be
|
||||
silently ignored if the latch is already locked.
|
||||
|
||||
``SDTX_IOCTL_LATCH_UNLOCK``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x24)``.
|
||||
|
||||
Unlocks the latch, causing the detachment procedure to open the latch on
|
||||
timeout. The latch is unlocked by default. This command will not open the
|
||||
latch when sent during an ongoing detachment process. It will be silently
|
||||
ignored if the latch is already unlocked.
|
||||
|
||||
``SDTX_IOCTL_LATCH_REQUEST``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x25)``.
|
||||
|
||||
Generic latch request. Behavior depends on the context: If no
|
||||
detachment-process is active, detachment is requested. Otherwise the
|
||||
currently active detachment-process will be aborted.
|
||||
|
||||
If a detachment process is canceled by this operation, a generic detachment
|
||||
request event (``SDTX_EVENT_REQUEST``) will be sent.
|
||||
|
||||
This essentially behaves the same as a detachment button press.
|
||||
|
||||
``SDTX_IOCTL_LATCH_CONFIRM``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x26)``.
|
||||
|
||||
Acknowledges and confirms a latch request. If sent during an ongoing
|
||||
detachment process, this command causes the latch to be opened immediately.
|
||||
The latch will also be opened if it has been locked. In this case, the latch
|
||||
lock is reset to the unlocked state.
|
||||
|
||||
This command will be silently ignored if there is currently no detachment
|
||||
procedure in progress.
|
||||
|
||||
``SDTX_IOCTL_LATCH_HEARTBEAT``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x27)``.
|
||||
|
||||
Sends a heartbeat, essentially resetting the detachment timeout. This
|
||||
command can be used to keep the detachment process alive while work required
|
||||
for the detachment to succeed is still in progress.
|
||||
|
||||
This command will be silently ignored if there is currently no detachment
|
||||
procedure in progress.
|
||||
|
||||
``SDTX_IOCTL_LATCH_CANCEL``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IO(0xA5, 0x28)``.
|
||||
|
||||
Cancels detachment in progress (if any). If a detachment process is canceled
|
||||
by this operation, a generic detachment request event
|
||||
(``SDTX_EVENT_REQUEST``) will be sent.
|
||||
|
||||
This command will be silently ignored if there is currently no detachment
|
||||
procedure in progress.
|
||||
|
||||
``SDTX_IOCTL_GET_BASE_INFO``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IOR(0xA5, 0x29, struct sdtx_base_info)``.
|
||||
|
||||
Get the current base connection state (i.e. attached/detached) and the type
|
||||
of the base connected to the clipboard. This is command essentially provides
|
||||
a way to query the information provided by the base connection change event
|
||||
(``SDTX_EVENT_BASE_CONNECTION``).
|
||||
|
||||
Possible values for ``struct sdtx_base_info.state`` are:
|
||||
|
||||
* ``SDTX_BASE_DETACHED``,
|
||||
* ``SDTX_BASE_ATTACHED``, and
|
||||
* ``SDTX_DETACH_NOT_FEASIBLE``.
|
||||
|
||||
Other values are reserved for future use.
|
||||
|
||||
``SDTX_IOCTL_GET_DEVICE_MODE``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IOR(0xA5, 0x2A, __u16)``.
|
||||
|
||||
Returns the device operation mode, indicating if and how the base is
|
||||
attached to the clipboard. This is command essentially provides a way to
|
||||
query the information provided by the device mode change event
|
||||
(``SDTX_EVENT_DEVICE_MODE``).
|
||||
|
||||
Returned values are:
|
||||
|
||||
* ``SDTX_DEVICE_MODE_LAPTOP``
|
||||
* ``SDTX_DEVICE_MODE_TABLET``
|
||||
* ``SDTX_DEVICE_MODE_STUDIO``
|
||||
|
||||
See |sdtx_device_mode| for details. Other values are reserved for future
|
||||
use.
|
||||
|
||||
|
||||
``SDTX_IOCTL_GET_LATCH_STATUS``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Defined as ``_IOR(0xA5, 0x2B, __u16)``.
|
||||
|
||||
Get the current latch status or (presumably) the last error encountered when
|
||||
trying to open/close the latch. This is command essentially provides a way
|
||||
to query the information provided by the latch status change event
|
||||
(``SDTX_EVENT_LATCH_STATUS``).
|
||||
|
||||
Returned values are:
|
||||
|
||||
* ``SDTX_LATCH_CLOSED``,
|
||||
* ``SDTX_LATCH_OPENED``,
|
||||
* ``SDTX_ERR_FAILED_TO_OPEN``,
|
||||
* ``SDTX_ERR_FAILED_TO_REMAIN_OPEN``, and
|
||||
* ``SDTX_ERR_FAILED_TO_CLOSE``.
|
||||
|
||||
Other values are reserved for future use.
|
||||
|
||||
A Note on Base IDs
|
||||
------------------
|
||||
|
||||
Base types/IDs provided via ``SDTX_EVENT_BASE_CONNECTION`` or
|
||||
``SDTX_IOCTL_GET_BASE_INFO`` are directly forwarded from the EC in the lower
|
||||
byte of the combined |__u16| value, with the driver storing the EC type from
|
||||
which this ID comes in the high byte (without this, base IDs over different
|
||||
types of ECs may be overlapping).
|
||||
|
||||
The ``SDTX_DEVICE_TYPE()`` macro can be used to determine the EC device
|
||||
type. This can be one of
|
||||
|
||||
* ``SDTX_DEVICE_TYPE_HID``, for Surface Aggregator Module over HID, and
|
||||
|
||||
* ``SDTX_DEVICE_TYPE_SSH``, for Surface Aggregator Module over Surface Serial
|
||||
Hub.
|
||||
|
||||
Note that currently only the ``SSH`` type EC is supported, however ``HID``
|
||||
type is reserved for future use.
|
||||
|
||||
Structures and Enums
|
||||
--------------------
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/surface_aggregator/dtx.h
|
||||
|
||||
API Users
|
||||
=========
|
||||
|
||||
A user-space daemon utilizing this API can be found at
|
||||
https://github.com/linux-surface/surface-dtx-daemon.
|
@ -11,6 +11,7 @@ This is the documentation for client drivers themselves. Refer to
|
||||
:maxdepth: 1
|
||||
|
||||
cdev
|
||||
dtx
|
||||
san
|
||||
|
||||
.. only:: subproject and html
|
||||
|
@ -327,6 +327,8 @@ Code Seq# Include File Comments
|
||||
0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
|
||||
0xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator
|
||||
<mailto:luzmaximilian@gmail.com>
|
||||
0xA5 20-2F linux/surface_aggregator/dtx.h Microsoft Surface DTX driver
|
||||
<mailto:luzmaximilian@gmail.com>
|
||||
0xAA 00-3F linux/uapi/linux/userfaultfd.h
|
||||
0xAB 00-1F linux/nbd.h
|
||||
0xAC 00-1F linux/raw.h
|
||||
|
49
MAINTAINERS
49
MAINTAINERS
@ -573,6 +573,12 @@ S: Maintained
|
||||
F: Documentation/scsi/advansys.rst
|
||||
F: drivers/scsi/advansys.c
|
||||
|
||||
ADVANTECH SWBTN DRIVER
|
||||
M: Andrea Ho <Andrea.Ho@advantech.com.tw>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/adv_swbutton.c
|
||||
|
||||
ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346)
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
S: Supported
|
||||
@ -697,6 +703,11 @@ S: Maintained
|
||||
F: Documentation/i2c/busses/i2c-ali1563.rst
|
||||
F: drivers/i2c/busses/i2c-ali1563.c
|
||||
|
||||
ALIENWARE WMI DRIVER
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell/alienware-wmi.c
|
||||
|
||||
ALL SENSORS DLH SERIES PRESSURE SENSORS DRIVER
|
||||
M: Tomislav Denis <tomislav.denis@avl.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@ -5043,19 +5054,19 @@ F: drivers/platform/x86/dell/dell_rbu.c
|
||||
|
||||
DELL SMBIOS DRIVER
|
||||
M: Pali Rohár <pali@kernel.org>
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell/dell-smbios.*
|
||||
|
||||
DELL SMBIOS SMM DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell/dell-smbios-smm.c
|
||||
|
||||
DELL SMBIOS WMI DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell/dell-smbios-wmi.c
|
||||
@ -5069,14 +5080,14 @@ F: Documentation/driver-api/dcdbas.rst
|
||||
F: drivers/platform/x86/dell/dcdbas.*
|
||||
|
||||
DELL WMI DESCRIPTOR DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell/dell-wmi-descriptor.c
|
||||
|
||||
DELL WMI SYSMAN DRIVER
|
||||
M: Divya Bharathi <divya.bharathi@dell.com>
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
M: Prasanth Ksr <prasanth.ksr@dell.com>
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-class-firmware-attributes
|
||||
@ -7551,6 +7562,12 @@ F: Documentation/filesystems/gfs2*
|
||||
F: fs/gfs2/
|
||||
F: include/uapi/linux/gfs2_ondisk.h
|
||||
|
||||
GIGABYTE WMI DRIVER
|
||||
M: Thomas Weißschuh <thomas@weissschuh.net>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/gigabyte-wmi.c
|
||||
|
||||
GNSS SUBSYSTEM
|
||||
M: Johan Hovold <johan@kernel.org>
|
||||
S: Maintained
|
||||
@ -9141,6 +9158,7 @@ M: Rajneesh Bhardwaj <irenic.rajneesh@gmail.com>
|
||||
M: David E Box <david.e.box@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-platform-intel-pmc
|
||||
F: drivers/platform/x86/intel_pmc_core*
|
||||
|
||||
INTEL PMIC GPIO DRIVERS
|
||||
@ -9251,7 +9269,7 @@ W: https://slimbootloader.github.io/security/firmware-update.html
|
||||
F: drivers/platform/x86/intel-wmi-sbl-fw-update.c
|
||||
|
||||
INTEL WMI THUNDERBOLT FORCE POWER DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
L: Dell.Client.Kernel@dell.com
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel-wmi-thunderbolt.c
|
||||
|
||||
@ -11452,8 +11470,8 @@ Q: https://patchwork.kernel.org/project/netdevbpf/list/
|
||||
F: drivers/net/ethernet/mellanox/mlxfw/
|
||||
|
||||
MELLANOX HARDWARE PLATFORM SUPPORT
|
||||
M: Andy Shevchenko <andy@infradead.org>
|
||||
M: Darren Hart <dvhart@infradead.org>
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Mark Gross <mgross@linux.intel.com>
|
||||
M: Vadim Pasternak <vadimp@nvidia.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Supported
|
||||
@ -11876,6 +11894,14 @@ F: drivers/scsi/smartpqi/smartpqi*.[ch]
|
||||
F: include/linux/cciss*.h
|
||||
F: include/uapi/linux/cciss*.h
|
||||
|
||||
MICROSOFT SURFACE DTX DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/driver-api/surface_aggregator/clients/dtx.rst
|
||||
F: drivers/platform/surface/surface_dtx.c
|
||||
F: include/uapi/linux/surface_aggregator/dtx.h
|
||||
|
||||
MICROSOFT SURFACE GPE LID SUPPORT DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -11897,6 +11923,12 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/surface/surface_hotplug.c
|
||||
|
||||
MICROSOFT SURFACE PLATFORM PROFILE DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/surface/surface_platform_profile.c
|
||||
|
||||
MICROSOFT SURFACE PRO 3 BUTTON DRIVER
|
||||
M: Chen Yu <yu.c.chen@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -11912,6 +11944,7 @@ F: Documentation/driver-api/surface_aggregator/
|
||||
F: drivers/platform/surface/aggregator/
|
||||
F: drivers/platform/surface/surface_acpi_notify.c
|
||||
F: drivers/platform/surface/surface_aggregator_cdev.c
|
||||
F: drivers/platform/surface/surface_aggregator_registry.c
|
||||
F: include/linux/surface_acpi_notify.h
|
||||
F: include/linux/surface_aggregator/
|
||||
F: include/uapi/linux/surface_aggregator/
|
||||
|
@ -49,10 +49,14 @@ enum pmt_quirks {
|
||||
|
||||
/* Use shift instead of mask to read discovery table offset */
|
||||
PMT_QUIRK_TABLE_SHIFT = BIT(2),
|
||||
|
||||
/* DVSEC not present (provided in driver data) */
|
||||
PMT_QUIRK_NO_DVSEC = BIT(3),
|
||||
};
|
||||
|
||||
struct pmt_platform_info {
|
||||
unsigned long quirks;
|
||||
struct intel_dvsec_header **capabilities;
|
||||
};
|
||||
|
||||
static const struct pmt_platform_info tgl_info = {
|
||||
@ -60,6 +64,26 @@ static const struct pmt_platform_info tgl_info = {
|
||||
PMT_QUIRK_TABLE_SHIFT,
|
||||
};
|
||||
|
||||
/* DG1 Platform with DVSEC quirk*/
|
||||
static struct intel_dvsec_header dg1_telemetry = {
|
||||
.length = 0x10,
|
||||
.id = 2,
|
||||
.num_entries = 1,
|
||||
.entry_size = 3,
|
||||
.tbir = 0,
|
||||
.offset = 0x466000,
|
||||
};
|
||||
|
||||
static struct intel_dvsec_header *dg1_capabilities[] = {
|
||||
&dg1_telemetry,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct pmt_platform_info dg1_info = {
|
||||
.quirks = PMT_QUIRK_NO_DVSEC,
|
||||
.capabilities = dg1_capabilities,
|
||||
};
|
||||
|
||||
static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header,
|
||||
unsigned long quirks)
|
||||
{
|
||||
@ -79,19 +103,18 @@ static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header,
|
||||
case DVSEC_INTEL_ID_WATCHER:
|
||||
if (quirks & PMT_QUIRK_NO_WATCHER) {
|
||||
dev_info(dev, "Watcher not supported\n");
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
name = "pmt_watcher";
|
||||
break;
|
||||
case DVSEC_INTEL_ID_CRASHLOG:
|
||||
if (quirks & PMT_QUIRK_NO_CRASHLOG) {
|
||||
dev_info(dev, "Crashlog not supported\n");
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
name = "pmt_crashlog";
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unrecognized PMT capability: %d\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -148,41 +171,54 @@ static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (info)
|
||||
quirks = info->quirks;
|
||||
|
||||
do {
|
||||
struct intel_dvsec_header header;
|
||||
u32 table;
|
||||
u16 vid;
|
||||
if (info && (info->quirks & PMT_QUIRK_NO_DVSEC)) {
|
||||
struct intel_dvsec_header **header;
|
||||
|
||||
pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC);
|
||||
if (!pos)
|
||||
break;
|
||||
header = info->capabilities;
|
||||
while (*header) {
|
||||
ret = pmt_add_dev(pdev, *header, quirks);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev,
|
||||
"Failed to add device for DVSEC id %d\n",
|
||||
(*header)->id);
|
||||
else
|
||||
found_devices = true;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid);
|
||||
if (vid != PCI_VENDOR_ID_INTEL)
|
||||
continue;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2,
|
||||
&header.id);
|
||||
pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES,
|
||||
&header.num_entries);
|
||||
pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE,
|
||||
&header.entry_size);
|
||||
pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE,
|
||||
&table);
|
||||
|
||||
header.tbir = INTEL_DVSEC_TABLE_BAR(table);
|
||||
header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
|
||||
|
||||
ret = pmt_add_dev(pdev, &header, quirks);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Failed to add device for DVSEC id %d\n",
|
||||
header.id);
|
||||
continue;
|
||||
++header;
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
struct intel_dvsec_header header;
|
||||
u32 table;
|
||||
u16 vid;
|
||||
|
||||
found_devices = true;
|
||||
} while (true);
|
||||
pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC);
|
||||
if (!pos)
|
||||
break;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid);
|
||||
if (vid != PCI_VENDOR_ID_INTEL)
|
||||
continue;
|
||||
|
||||
pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2,
|
||||
&header.id);
|
||||
pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES,
|
||||
&header.num_entries);
|
||||
pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE,
|
||||
&header.entry_size);
|
||||
pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE,
|
||||
&table);
|
||||
|
||||
header.tbir = INTEL_DVSEC_TABLE_BAR(table);
|
||||
header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
|
||||
|
||||
ret = pmt_add_dev(pdev, &header, quirks);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
found_devices = true;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
if (!found_devices)
|
||||
return -ENODEV;
|
||||
@ -200,10 +236,12 @@ static void pmt_pci_remove(struct pci_dev *pdev)
|
||||
}
|
||||
|
||||
#define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d
|
||||
#define PCI_DEVICE_ID_INTEL_PMT_DG1 0x490e
|
||||
#define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7
|
||||
#define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d
|
||||
static const struct pci_device_id pmt_pci_ids[] = {
|
||||
{ PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PMT_DG1, &dg1_info) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) },
|
||||
{ PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) },
|
||||
{ }
|
||||
|
@ -208,7 +208,7 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev,
|
||||
* 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits
|
||||
* are a thermometer code indicating key programming has completed for
|
||||
* key n (same encodings as the start bits). This allows for detection
|
||||
* of an interruption in the progamming process which has left the key
|
||||
* of an interruption in the programming process which has left the key
|
||||
* partially programmed (and thus invalid). The process is to burn the
|
||||
* eFuse for the new key start bit, burn the key eFuses, then burn the
|
||||
* eFuse for the new key complete bit.
|
||||
|
@ -683,13 +683,13 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
|
||||
|
||||
err = devm_request_irq(&pdev->dev, priv->irq,
|
||||
mlxreg_hotplug_irq_handler, IRQF_TRIGGER_FALLING
|
||||
| IRQF_SHARED, "mlxreg-hotplug", priv);
|
||||
| IRQF_SHARED | IRQF_NO_AUTOEN,
|
||||
"mlxreg-hotplug", priv);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to request irq: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
disable_irq(priv->irq);
|
||||
spin_lock_init(&priv->lock);
|
||||
INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler);
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
@ -77,6 +77,53 @@ config SURFACE_AGGREGATOR_CDEV
|
||||
The provided interface is intended for debugging and development only,
|
||||
and should not be used otherwise.
|
||||
|
||||
config SURFACE_AGGREGATOR_REGISTRY
|
||||
tristate "Surface System Aggregator Module Device Registry"
|
||||
depends on SURFACE_AGGREGATOR
|
||||
depends on SURFACE_AGGREGATOR_BUS
|
||||
help
|
||||
Device-registry and device-hubs for Surface System Aggregator Module
|
||||
(SSAM) devices.
|
||||
|
||||
Provides a module and driver which act as a device-registry for SSAM
|
||||
client devices that cannot be detected automatically, e.g. via ACPI.
|
||||
Such devices are instead provided via this registry and attached via
|
||||
device hubs, also provided in this module.
|
||||
|
||||
Devices provided via this registry are:
|
||||
- Platform profile (performance-/cooling-mode) device (5th- and later
|
||||
generations).
|
||||
- Battery/AC devices (7th-generation).
|
||||
- HID input devices (7th-generation).
|
||||
|
||||
Select M (recommended) or Y here if you want support for the above
|
||||
mentioned devices on the corresponding Surface models. Without this
|
||||
module, the respective devices will not be instantiated and thus any
|
||||
functionality provided by them will be missing, even when drivers for
|
||||
these devices are present. In other words, this module only provides
|
||||
the respective client devices. Drivers for these devices still need to
|
||||
be selected via the other options.
|
||||
|
||||
config SURFACE_DTX
|
||||
tristate "Surface DTX (Detachment System) Driver"
|
||||
depends on SURFACE_AGGREGATOR
|
||||
depends on INPUT
|
||||
help
|
||||
Driver for the Surface Book clipboard detachment system (DTX).
|
||||
|
||||
On the Surface Book series devices, the display part containing the
|
||||
CPU (called the clipboard) can be detached from the base (containing a
|
||||
battery, the keyboard, and, optionally, a discrete GPU) by (if
|
||||
necessary) unlocking and opening the latch connecting both parts.
|
||||
|
||||
This driver provides a user-space interface that can influence the
|
||||
behavior of this process, which includes the option to abort it in
|
||||
case the base is still in use or speed it up in case it is not.
|
||||
|
||||
Note that this module can be built without support for the Surface
|
||||
Aggregator Bus (i.e. CONFIG_SURFACE_AGGREGATOR_BUS=n). In that case,
|
||||
some devices, specifically the Surface Book 3, will not be supported.
|
||||
|
||||
config SURFACE_GPE
|
||||
tristate "Surface GPE/Lid Support Driver"
|
||||
depends on DMI
|
||||
@ -105,6 +152,28 @@ config SURFACE_HOTPLUG
|
||||
Select M or Y here, if you want to (fully) support hot-plugging of
|
||||
dGPU devices on the Surface Book 2 and/or 3 during D3cold.
|
||||
|
||||
config SURFACE_PLATFORM_PROFILE
|
||||
tristate "Surface Platform Profile Driver"
|
||||
depends on SURFACE_AGGREGATOR_REGISTRY
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
help
|
||||
Provides support for the ACPI platform profile on 5th- and later
|
||||
generation Microsoft Surface devices.
|
||||
|
||||
More specifically, this driver provides ACPI platform profile support
|
||||
on Microsoft Surface devices with a Surface System Aggregator Module
|
||||
(SSAM) connected via the Surface Serial Hub (SSH / SAM-over-SSH). In
|
||||
other words, this driver provides platform profile support on the
|
||||
Surface Pro 5, Surface Book 2, Surface Laptop, Surface Laptop Go and
|
||||
later. On those devices, the platform profile can significantly
|
||||
influence cooling behavior, e.g. setting it to 'quiet' (default) or
|
||||
'low-power' can significantly limit performance of the discrete GPU on
|
||||
Surface Books, while in turn leading to lower power consumption and/or
|
||||
less fan noise.
|
||||
|
||||
Select M or Y here, if you want to include ACPI platform profile
|
||||
support on the above mentioned devices.
|
||||
|
||||
config SURFACE_PRO3_BUTTON
|
||||
tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet"
|
||||
depends on INPUT
|
||||
|
@ -10,6 +10,9 @@ obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
|
||||
obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o
|
||||
obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/
|
||||
obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o
|
||||
obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o
|
||||
obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o
|
||||
obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
|
||||
obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
|
||||
obj-$(CONFIG_SURFACE_PLATFORM_PROFILE) += surface_platform_profile.o
|
||||
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
|
||||
|
@ -1040,7 +1040,7 @@ static int ssam_dsm_load_u32(acpi_handle handle, u64 funcs, u64 func, u32 *ret)
|
||||
union acpi_object *obj;
|
||||
u64 val;
|
||||
|
||||
if (!(funcs & BIT(func)))
|
||||
if (!(funcs & BIT_ULL(func)))
|
||||
return 0; /* Not supported, leave *ret at its default value */
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(handle, &SSAM_SSH_DSM_GUID,
|
||||
@ -1750,35 +1750,35 @@ EXPORT_SYMBOL_GPL(ssam_request_sync_with_buffer);
|
||||
|
||||
/* -- Internal SAM requests. ------------------------------------------------ */
|
||||
|
||||
static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_get_firmware_version, __le32, {
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_get_firmware_version, __le32, {
|
||||
.target_category = SSAM_SSH_TC_SAM,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x13,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_off, u8, {
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_off, u8, {
|
||||
.target_category = SSAM_SSH_TC_SAM,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x15,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_on, u8, {
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_on, u8, {
|
||||
.target_category = SSAM_SSH_TC_SAM,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x16,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_exit, u8, {
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_exit, u8, {
|
||||
.target_category = SSAM_SSH_TC_SAM,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x33,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
static SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_entry, u8, {
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_entry, u8, {
|
||||
.target_category = SSAM_SSH_TC_SAM,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x34,
|
||||
@ -2483,7 +2483,8 @@ int ssam_irq_setup(struct ssam_controller *ctrl)
|
||||
* interrupt, and let the SAM resume callback during the controller
|
||||
* resume process clear it.
|
||||
*/
|
||||
const int irqf = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING;
|
||||
const int irqf = IRQF_SHARED | IRQF_ONESHOT |
|
||||
IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN;
|
||||
|
||||
gpiod = gpiod_get(dev, "ssam_wakeup-int", GPIOD_ASIS);
|
||||
if (IS_ERR(gpiod))
|
||||
@ -2501,7 +2502,6 @@ int ssam_irq_setup(struct ssam_controller *ctrl)
|
||||
return status;
|
||||
|
||||
ctrl->irq.num = irq;
|
||||
disable_irq(ctrl->irq.num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
626
drivers/platform/surface/surface_aggregator_registry.c
Normal file
626
drivers/platform/surface/surface_aggregator_registry.c
Normal file
@ -0,0 +1,626 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Surface System Aggregator Module (SSAM) client device registry.
|
||||
*
|
||||
* Registry for non-platform/non-ACPI SSAM client devices, i.e. devices that
|
||||
* cannot be auto-detected. Provides device-hubs and performs instantiation
|
||||
* for these devices.
|
||||
*
|
||||
* Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/surface_aggregator/controller.h>
|
||||
#include <linux/surface_aggregator/device.h>
|
||||
|
||||
|
||||
/* -- Device registry. ------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* SSAM device names follow the SSAM module alias, meaning they are prefixed
|
||||
* with 'ssam:', followed by domain, category, target ID, instance ID, and
|
||||
* function, each encoded as two-digit hexadecimal, separated by ':'. In other
|
||||
* words, it follows the scheme
|
||||
*
|
||||
* ssam:dd:cc:tt:ii:ff
|
||||
*
|
||||
* Where, 'dd', 'cc', 'tt', 'ii', and 'ff' are the two-digit hexadecimal
|
||||
* values mentioned above, respectively.
|
||||
*/
|
||||
|
||||
/* Root node. */
|
||||
static const struct software_node ssam_node_root = {
|
||||
.name = "ssam_platform_hub",
|
||||
};
|
||||
|
||||
/* Base device hub (devices attached to Surface Book 3 base). */
|
||||
static const struct software_node ssam_node_hub_base = {
|
||||
.name = "ssam:00:00:02:00:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* AC adapter. */
|
||||
static const struct software_node ssam_node_bat_ac = {
|
||||
.name = "ssam:01:02:01:01:01",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* Primary battery. */
|
||||
static const struct software_node ssam_node_bat_main = {
|
||||
.name = "ssam:01:02:01:01:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* Secondary battery (Surface Book 3). */
|
||||
static const struct software_node ssam_node_bat_sb3base = {
|
||||
.name = "ssam:01:02:02:01:00",
|
||||
.parent = &ssam_node_hub_base,
|
||||
};
|
||||
|
||||
/* Platform profile / performance-mode device. */
|
||||
static const struct software_node ssam_node_tmp_pprof = {
|
||||
.name = "ssam:01:03:01:00:01",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* DTX / detachment-system device (Surface Book 3). */
|
||||
static const struct software_node ssam_node_bas_dtx = {
|
||||
.name = "ssam:01:11:01:00:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID keyboard. */
|
||||
static const struct software_node ssam_node_hid_main_keyboard = {
|
||||
.name = "ssam:01:15:02:01:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID touchpad. */
|
||||
static const struct software_node ssam_node_hid_main_touchpad = {
|
||||
.name = "ssam:01:15:02:03:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID device instance 5 (unknown HID device). */
|
||||
static const struct software_node ssam_node_hid_main_iid5 = {
|
||||
.name = "ssam:01:15:02:05:00",
|
||||
.parent = &ssam_node_root,
|
||||
};
|
||||
|
||||
/* HID keyboard (base hub). */
|
||||
static const struct software_node ssam_node_hid_base_keyboard = {
|
||||
.name = "ssam:01:15:02:01:00",
|
||||
.parent = &ssam_node_hub_base,
|
||||
};
|
||||
|
||||
/* HID touchpad (base hub). */
|
||||
static const struct software_node ssam_node_hid_base_touchpad = {
|
||||
.name = "ssam:01:15:02:03:00",
|
||||
.parent = &ssam_node_hub_base,
|
||||
};
|
||||
|
||||
/* HID device instance 5 (unknown HID device, base hub). */
|
||||
static const struct software_node ssam_node_hid_base_iid5 = {
|
||||
.name = "ssam:01:15:02:05:00",
|
||||
.parent = &ssam_node_hub_base,
|
||||
};
|
||||
|
||||
/* HID device instance 6 (unknown HID device, base hub). */
|
||||
static const struct software_node ssam_node_hid_base_iid6 = {
|
||||
.name = "ssam:01:15:02:06:00",
|
||||
.parent = &ssam_node_hub_base,
|
||||
};
|
||||
|
||||
/* Devices for Surface Book 2. */
|
||||
static const struct software_node *ssam_node_group_sb2[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Book 3. */
|
||||
static const struct software_node *ssam_node_group_sb3[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_hub_base,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_bat_sb3base,
|
||||
&ssam_node_tmp_pprof,
|
||||
&ssam_node_bas_dtx,
|
||||
&ssam_node_hid_base_keyboard,
|
||||
&ssam_node_hid_base_touchpad,
|
||||
&ssam_node_hid_base_iid5,
|
||||
&ssam_node_hid_base_iid6,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop 1. */
|
||||
static const struct software_node *ssam_node_group_sl1[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop 2. */
|
||||
static const struct software_node *ssam_node_group_sl2[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop 3. */
|
||||
static const struct software_node *ssam_node_group_sl3[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_pprof,
|
||||
&ssam_node_hid_main_keyboard,
|
||||
&ssam_node_hid_main_touchpad,
|
||||
&ssam_node_hid_main_iid5,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Laptop Go. */
|
||||
static const struct software_node *ssam_node_group_slg1[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Pro 5. */
|
||||
static const struct software_node *ssam_node_group_sp5[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Pro 6. */
|
||||
static const struct software_node *ssam_node_group_sp6[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Devices for Surface Pro 7 and Surface Pro 7+. */
|
||||
static const struct software_node *ssam_node_group_sp7[] = {
|
||||
&ssam_node_root,
|
||||
&ssam_node_bat_ac,
|
||||
&ssam_node_bat_main,
|
||||
&ssam_node_tmp_pprof,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
/* -- Device registry helper functions. ------------------------------------- */
|
||||
|
||||
static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid)
|
||||
{
|
||||
u8 d, tc, tid, iid, fn;
|
||||
int n;
|
||||
|
||||
n = sscanf(str, "ssam:%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn);
|
||||
if (n != 5)
|
||||
return -EINVAL;
|
||||
|
||||
uid->domain = d;
|
||||
uid->category = tc;
|
||||
uid->target = tid;
|
||||
uid->instance = iid;
|
||||
uid->function = fn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssam_hub_remove_devices_fn(struct device *dev, void *data)
|
||||
{
|
||||
if (!is_ssam_device(dev))
|
||||
return 0;
|
||||
|
||||
ssam_device_remove(to_ssam_device(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssam_hub_remove_devices(struct device *parent)
|
||||
{
|
||||
device_for_each_child_reverse(parent, NULL, ssam_hub_remove_devices_fn);
|
||||
}
|
||||
|
||||
static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl,
|
||||
struct fwnode_handle *node)
|
||||
{
|
||||
struct ssam_device_uid uid;
|
||||
struct ssam_device *sdev;
|
||||
int status;
|
||||
|
||||
status = ssam_uid_from_string(fwnode_get_name(node), &uid);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
sdev = ssam_device_alloc(ctrl, uid);
|
||||
if (!sdev)
|
||||
return -ENOMEM;
|
||||
|
||||
sdev->dev.parent = parent;
|
||||
sdev->dev.fwnode = node;
|
||||
|
||||
status = ssam_device_add(sdev);
|
||||
if (status)
|
||||
ssam_device_put(sdev);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *ctrl,
|
||||
struct fwnode_handle *node)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
int status;
|
||||
|
||||
fwnode_for_each_child_node(node, child) {
|
||||
/*
|
||||
* Try to add the device specified in the firmware node. If
|
||||
* this fails with -EINVAL, the node does not specify any SSAM
|
||||
* device, so ignore it and continue with the next one.
|
||||
*/
|
||||
|
||||
status = ssam_hub_add_device(parent, ctrl, child);
|
||||
if (status && status != -EINVAL)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
ssam_hub_remove_devices(parent);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* -- SSAM base-hub driver. ------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Some devices (especially battery) may need a bit of time to be fully usable
|
||||
* after being (re-)connected. This delay has been determined via
|
||||
* experimentation.
|
||||
*/
|
||||
#define SSAM_BASE_UPDATE_CONNECT_DELAY msecs_to_jiffies(2500)
|
||||
|
||||
enum ssam_base_hub_state {
|
||||
SSAM_BASE_HUB_UNINITIALIZED,
|
||||
SSAM_BASE_HUB_CONNECTED,
|
||||
SSAM_BASE_HUB_DISCONNECTED,
|
||||
};
|
||||
|
||||
struct ssam_base_hub {
|
||||
struct ssam_device *sdev;
|
||||
|
||||
enum ssam_base_hub_state state;
|
||||
struct delayed_work update_work;
|
||||
|
||||
struct ssam_event_notifier notif;
|
||||
};
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, {
|
||||
.target_category = SSAM_SSH_TC_BAS,
|
||||
.target_id = 0x01,
|
||||
.command_id = 0x0d,
|
||||
.instance_id = 0x00,
|
||||
});
|
||||
|
||||
#define SSAM_BAS_OPMODE_TABLET 0x00
|
||||
#define SSAM_EVENT_BAS_CID_CONNECTION 0x0c
|
||||
|
||||
static int ssam_base_hub_query_state(struct ssam_base_hub *hub, enum ssam_base_hub_state *state)
|
||||
{
|
||||
u8 opmode;
|
||||
int status;
|
||||
|
||||
status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode);
|
||||
if (status < 0) {
|
||||
dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (opmode != SSAM_BAS_OPMODE_TABLET)
|
||||
*state = SSAM_BASE_HUB_CONNECTED;
|
||||
else
|
||||
*state = SSAM_BASE_HUB_DISCONNECTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ssam_base_hub_state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ssam_base_hub *hub = dev_get_drvdata(dev);
|
||||
bool connected = hub->state == SSAM_BASE_HUB_CONNECTED;
|
||||
|
||||
return sysfs_emit(buf, "%d\n", connected);
|
||||
}
|
||||
|
||||
static struct device_attribute ssam_base_hub_attr_state =
|
||||
__ATTR(state, 0444, ssam_base_hub_state_show, NULL);
|
||||
|
||||
static struct attribute *ssam_base_hub_attrs[] = {
|
||||
&ssam_base_hub_attr_state.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ssam_base_hub_group = {
|
||||
.attrs = ssam_base_hub_attrs,
|
||||
};
|
||||
|
||||
static void ssam_base_hub_update_workfn(struct work_struct *work)
|
||||
{
|
||||
struct ssam_base_hub *hub = container_of(work, struct ssam_base_hub, update_work.work);
|
||||
struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev);
|
||||
enum ssam_base_hub_state state;
|
||||
int status = 0;
|
||||
|
||||
status = ssam_base_hub_query_state(hub, &state);
|
||||
if (status)
|
||||
return;
|
||||
|
||||
if (hub->state == state)
|
||||
return;
|
||||
hub->state = state;
|
||||
|
||||
if (hub->state == SSAM_BASE_HUB_CONNECTED)
|
||||
status = ssam_hub_add_devices(&hub->sdev->dev, hub->sdev->ctrl, node);
|
||||
else
|
||||
ssam_hub_remove_devices(&hub->sdev->dev);
|
||||
|
||||
if (status)
|
||||
dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", status);
|
||||
}
|
||||
|
||||
static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event)
|
||||
{
|
||||
struct ssam_base_hub *hub = container_of(nf, struct ssam_base_hub, notif);
|
||||
unsigned long delay;
|
||||
|
||||
if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION)
|
||||
return 0;
|
||||
|
||||
if (event->length < 1) {
|
||||
dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delay update when the base is being connected to give devices/EC
|
||||
* some time to set up.
|
||||
*/
|
||||
delay = event->data[0] ? SSAM_BASE_UPDATE_CONNECT_DELAY : 0;
|
||||
|
||||
schedule_delayed_work(&hub->update_work, delay);
|
||||
|
||||
/*
|
||||
* Do not return SSAM_NOTIF_HANDLED: The event should be picked up and
|
||||
* consumed by the detachment system driver. We're just a (more or less)
|
||||
* silent observer.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ssam_base_hub_resume(struct device *dev)
|
||||
{
|
||||
struct ssam_base_hub *hub = dev_get_drvdata(dev);
|
||||
|
||||
schedule_delayed_work(&hub->update_work, 0);
|
||||
return 0;
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(ssam_base_hub_pm_ops, NULL, ssam_base_hub_resume);
|
||||
|
||||
static int ssam_base_hub_probe(struct ssam_device *sdev)
|
||||
{
|
||||
struct ssam_base_hub *hub;
|
||||
int status;
|
||||
|
||||
hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL);
|
||||
if (!hub)
|
||||
return -ENOMEM;
|
||||
|
||||
hub->sdev = sdev;
|
||||
hub->state = SSAM_BASE_HUB_UNINITIALIZED;
|
||||
|
||||
hub->notif.base.priority = INT_MAX; /* This notifier should run first. */
|
||||
hub->notif.base.fn = ssam_base_hub_notif;
|
||||
hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM;
|
||||
hub->notif.event.id.target_category = SSAM_SSH_TC_BAS,
|
||||
hub->notif.event.id.instance = 0,
|
||||
hub->notif.event.mask = SSAM_EVENT_MASK_NONE;
|
||||
hub->notif.event.flags = SSAM_EVENT_SEQUENCED;
|
||||
|
||||
INIT_DELAYED_WORK(&hub->update_work, ssam_base_hub_update_workfn);
|
||||
|
||||
ssam_device_set_drvdata(sdev, hub);
|
||||
|
||||
status = ssam_notifier_register(sdev->ctrl, &hub->notif);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = sysfs_create_group(&sdev->dev.kobj, &ssam_base_hub_group);
|
||||
if (status)
|
||||
goto err;
|
||||
|
||||
schedule_delayed_work(&hub->update_work, 0);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
ssam_notifier_unregister(sdev->ctrl, &hub->notif);
|
||||
cancel_delayed_work_sync(&hub->update_work);
|
||||
ssam_hub_remove_devices(&sdev->dev);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void ssam_base_hub_remove(struct ssam_device *sdev)
|
||||
{
|
||||
struct ssam_base_hub *hub = ssam_device_get_drvdata(sdev);
|
||||
|
||||
sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group);
|
||||
|
||||
ssam_notifier_unregister(sdev->ctrl, &hub->notif);
|
||||
cancel_delayed_work_sync(&hub->update_work);
|
||||
ssam_hub_remove_devices(&sdev->dev);
|
||||
}
|
||||
|
||||
static const struct ssam_device_id ssam_base_hub_match[] = {
|
||||
{ SSAM_VDEV(HUB, 0x02, SSAM_ANY_IID, 0x00) },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct ssam_device_driver ssam_base_hub_driver = {
|
||||
.probe = ssam_base_hub_probe,
|
||||
.remove = ssam_base_hub_remove,
|
||||
.match_table = ssam_base_hub_match,
|
||||
.driver = {
|
||||
.name = "surface_aggregator_base_hub",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &ssam_base_hub_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* -- SSAM platform/meta-hub driver. ---------------------------------------- */
|
||||
|
||||
static const struct acpi_device_id ssam_platform_hub_match[] = {
|
||||
/* Surface Pro 4, 5, and 6 (OMBR < 0x10) */
|
||||
{ "MSHW0081", (unsigned long)ssam_node_group_sp5 },
|
||||
|
||||
/* Surface Pro 6 (OMBR >= 0x10) */
|
||||
{ "MSHW0111", (unsigned long)ssam_node_group_sp6 },
|
||||
|
||||
/* Surface Pro 7 */
|
||||
{ "MSHW0116", (unsigned long)ssam_node_group_sp7 },
|
||||
|
||||
/* Surface Pro 7+ */
|
||||
{ "MSHW0119", (unsigned long)ssam_node_group_sp7 },
|
||||
|
||||
/* Surface Book 2 */
|
||||
{ "MSHW0107", (unsigned long)ssam_node_group_sb2 },
|
||||
|
||||
/* Surface Book 3 */
|
||||
{ "MSHW0117", (unsigned long)ssam_node_group_sb3 },
|
||||
|
||||
/* Surface Laptop 1 */
|
||||
{ "MSHW0086", (unsigned long)ssam_node_group_sl1 },
|
||||
|
||||
/* Surface Laptop 2 */
|
||||
{ "MSHW0112", (unsigned long)ssam_node_group_sl2 },
|
||||
|
||||
/* Surface Laptop 3 (13", Intel) */
|
||||
{ "MSHW0114", (unsigned long)ssam_node_group_sl3 },
|
||||
|
||||
/* Surface Laptop 3 (15", AMD) */
|
||||
{ "MSHW0110", (unsigned long)ssam_node_group_sl3 },
|
||||
|
||||
/* Surface Laptop Go 1 */
|
||||
{ "MSHW0118", (unsigned long)ssam_node_group_slg1 },
|
||||
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match);
|
||||
|
||||
static int ssam_platform_hub_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct software_node **nodes;
|
||||
struct ssam_controller *ctrl;
|
||||
struct fwnode_handle *root;
|
||||
int status;
|
||||
|
||||
nodes = (const struct software_node **)acpi_device_get_match_data(&pdev->dev);
|
||||
if (!nodes)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* As we're adding the SSAM client devices as children under this device
|
||||
* and not the SSAM controller, we need to add a device link to the
|
||||
* controller to ensure that we remove all of our devices before the
|
||||
* controller is removed. This also guarantees proper ordering for
|
||||
* suspend/resume of the devices on this hub.
|
||||
*/
|
||||
ctrl = ssam_client_bind(&pdev->dev);
|
||||
if (IS_ERR(ctrl))
|
||||
return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
|
||||
|
||||
status = software_node_register_node_group(nodes);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
root = software_node_fwnode(&ssam_node_root);
|
||||
if (!root) {
|
||||
software_node_unregister_node_group(nodes);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
set_secondary_fwnode(&pdev->dev, root);
|
||||
|
||||
status = ssam_hub_add_devices(&pdev->dev, ctrl, root);
|
||||
if (status) {
|
||||
set_secondary_fwnode(&pdev->dev, NULL);
|
||||
software_node_unregister_node_group(nodes);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, nodes);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ssam_platform_hub_remove(struct platform_device *pdev)
|
||||
{
|
||||
const struct software_node **nodes = platform_get_drvdata(pdev);
|
||||
|
||||
ssam_hub_remove_devices(&pdev->dev);
|
||||
set_secondary_fwnode(&pdev->dev, NULL);
|
||||
software_node_unregister_node_group(nodes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssam_platform_hub_driver = {
|
||||
.probe = ssam_platform_hub_probe,
|
||||
.remove = ssam_platform_hub_remove,
|
||||
.driver = {
|
||||
.name = "surface_aggregator_platform_hub",
|
||||
.acpi_match_table = ssam_platform_hub_match,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* -- Module initialization. ------------------------------------------------ */
|
||||
|
||||
static int __init ssam_device_hub_init(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = platform_driver_register(&ssam_platform_hub_driver);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = ssam_device_driver_register(&ssam_base_hub_driver);
|
||||
if (status)
|
||||
platform_driver_unregister(&ssam_platform_hub_driver);
|
||||
|
||||
return status;
|
||||
}
|
||||
module_init(ssam_device_hub_init);
|
||||
|
||||
static void __exit ssam_device_hub_exit(void)
|
||||
{
|
||||
ssam_device_driver_unregister(&ssam_base_hub_driver);
|
||||
platform_driver_unregister(&ssam_platform_hub_driver);
|
||||
}
|
||||
module_exit(ssam_device_hub_exit);
|
||||
|
||||
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
|
||||
MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module");
|
||||
MODULE_LICENSE("GPL");
|
1289
drivers/platform/surface/surface_dtx.c
Normal file
1289
drivers/platform/surface/surface_dtx.c
Normal file
File diff suppressed because it is too large
Load Diff
190
drivers/platform/surface/surface_platform_profile.c
Normal file
190
drivers/platform/surface/surface_platform_profile.c
Normal file
@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Surface Platform Profile / Performance Mode driver for Surface System
|
||||
* Aggregator Module (thermal subsystem).
|
||||
*
|
||||
* Copyright (C) 2021 Maximilian Luz <luzmaximilian@gmail.com>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/surface_aggregator/device.h>
|
||||
|
||||
enum ssam_tmp_profile {
|
||||
SSAM_TMP_PROFILE_NORMAL = 1,
|
||||
SSAM_TMP_PROFILE_BATTERY_SAVER = 2,
|
||||
SSAM_TMP_PROFILE_BETTER_PERFORMANCE = 3,
|
||||
SSAM_TMP_PROFILE_BEST_PERFORMANCE = 4,
|
||||
};
|
||||
|
||||
struct ssam_tmp_profile_info {
|
||||
__le32 profile;
|
||||
__le16 unknown1;
|
||||
__le16 unknown2;
|
||||
} __packed;
|
||||
|
||||
struct ssam_tmp_profile_device {
|
||||
struct ssam_device *sdev;
|
||||
struct platform_profile_handler handler;
|
||||
};
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {
|
||||
.target_category = SSAM_SSH_TC_TMP,
|
||||
.command_id = 0x02,
|
||||
});
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_CL_W(__ssam_tmp_profile_set, __le32, {
|
||||
.target_category = SSAM_SSH_TC_TMP,
|
||||
.command_id = 0x03,
|
||||
});
|
||||
|
||||
static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile *p)
|
||||
{
|
||||
struct ssam_tmp_profile_info info;
|
||||
int status;
|
||||
|
||||
status = ssam_retry(__ssam_tmp_profile_get, sdev, &info);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
*p = le32_to_cpu(info.profile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssam_tmp_profile_set(struct ssam_device *sdev, enum ssam_tmp_profile p)
|
||||
{
|
||||
__le32 profile_le = cpu_to_le32(p);
|
||||
|
||||
return ssam_retry(__ssam_tmp_profile_set, sdev, &profile_le);
|
||||
}
|
||||
|
||||
static int convert_ssam_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p)
|
||||
{
|
||||
switch (p) {
|
||||
case SSAM_TMP_PROFILE_NORMAL:
|
||||
return PLATFORM_PROFILE_BALANCED;
|
||||
|
||||
case SSAM_TMP_PROFILE_BATTERY_SAVER:
|
||||
return PLATFORM_PROFILE_LOW_POWER;
|
||||
|
||||
case SSAM_TMP_PROFILE_BETTER_PERFORMANCE:
|
||||
return PLATFORM_PROFILE_BALANCED_PERFORMANCE;
|
||||
|
||||
case SSAM_TMP_PROFILE_BEST_PERFORMANCE:
|
||||
return PLATFORM_PROFILE_PERFORMANCE;
|
||||
|
||||
default:
|
||||
dev_err(&sdev->dev, "invalid performance profile: %d", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int convert_profile_to_ssam(struct ssam_device *sdev, enum platform_profile_option p)
|
||||
{
|
||||
switch (p) {
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
return SSAM_TMP_PROFILE_BATTERY_SAVER;
|
||||
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
return SSAM_TMP_PROFILE_NORMAL;
|
||||
|
||||
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
|
||||
return SSAM_TMP_PROFILE_BETTER_PERFORMANCE;
|
||||
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
return SSAM_TMP_PROFILE_BEST_PERFORMANCE;
|
||||
|
||||
default:
|
||||
/* This should have already been caught by platform_profile_store(). */
|
||||
WARN(true, "unsupported platform profile");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
struct ssam_tmp_profile_device *tpd;
|
||||
enum ssam_tmp_profile tp;
|
||||
int status;
|
||||
|
||||
tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
|
||||
|
||||
status = ssam_tmp_profile_get(tpd->sdev, &tp);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = convert_ssam_to_profile(tpd->sdev, tp);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
*profile = status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
struct ssam_tmp_profile_device *tpd;
|
||||
int tp;
|
||||
|
||||
tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
|
||||
|
||||
tp = convert_profile_to_ssam(tpd->sdev, profile);
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
|
||||
return ssam_tmp_profile_set(tpd->sdev, tp);
|
||||
}
|
||||
|
||||
static int surface_platform_profile_probe(struct ssam_device *sdev)
|
||||
{
|
||||
struct ssam_tmp_profile_device *tpd;
|
||||
|
||||
tpd = devm_kzalloc(&sdev->dev, sizeof(*tpd), GFP_KERNEL);
|
||||
if (!tpd)
|
||||
return -ENOMEM;
|
||||
|
||||
tpd->sdev = sdev;
|
||||
|
||||
tpd->handler.profile_get = ssam_platform_profile_get;
|
||||
tpd->handler.profile_set = ssam_platform_profile_set;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices);
|
||||
|
||||
platform_profile_register(&tpd->handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void surface_platform_profile_remove(struct ssam_device *sdev)
|
||||
{
|
||||
platform_profile_remove();
|
||||
}
|
||||
|
||||
static const struct ssam_device_id ssam_platform_profile_match[] = {
|
||||
{ SSAM_SDEV(TMP, 0x01, 0x00, 0x01) },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);
|
||||
|
||||
static struct ssam_device_driver surface_platform_profile = {
|
||||
.probe = surface_platform_profile_probe,
|
||||
.remove = surface_platform_profile_remove,
|
||||
.match_table = ssam_platform_profile_match,
|
||||
.driver = {
|
||||
.name = "surface_platform_profile",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
module_ssam_device_driver(surface_platform_profile);
|
||||
|
||||
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
|
||||
MODULE_DESCRIPTION("Platform Profile Support for Surface System Aggregator Module");
|
||||
MODULE_LICENSE("GPL");
|
@ -40,8 +40,6 @@ static const guid_t MSHW0040_DSM_UUID =
|
||||
#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2
|
||||
#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3
|
||||
|
||||
ACPI_MODULE_NAME("surface pro 3 button");
|
||||
|
||||
MODULE_AUTHOR("Chen Yu");
|
||||
MODULE_DESCRIPTION("Surface Pro3 Button Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -123,6 +123,17 @@ config XIAOMI_WMI
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called xiaomi-wmi.
|
||||
|
||||
config GIGABYTE_WMI
|
||||
tristate "Gigabyte WMI temperature driver"
|
||||
depends on ACPI_WMI
|
||||
depends on HWMON
|
||||
help
|
||||
Say Y here if you want to support WMI-based temperature reporting on
|
||||
Gigabyte mainboards.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called gigabyte-wmi.
|
||||
|
||||
config ACERHDF
|
||||
tristate "Acer Aspire One temperature and fan driver"
|
||||
depends on ACPI && THERMAL
|
||||
@ -193,6 +204,17 @@ config AMD_PMC
|
||||
If you choose to compile this driver as a module the module will be
|
||||
called amd-pmc.
|
||||
|
||||
config ADV_SWBUTTON
|
||||
tristate "Advantech ACPI Software Button Driver"
|
||||
depends on ACPI && INPUT
|
||||
help
|
||||
Say Y here to enable support for Advantech software defined
|
||||
button feature. More information can be found at
|
||||
<http://www.advantech.com.tw/products/>
|
||||
|
||||
To compile this driver as a module, choose M here. The module will
|
||||
be called adv_swbutton.
|
||||
|
||||
config APPLE_GMUX
|
||||
tristate "Apple Gmux Driver"
|
||||
depends on ACPI && PCI
|
||||
@ -410,6 +432,7 @@ config HP_WMI
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_SPARSEKMAP
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
help
|
||||
Say Y here if you want to support WMI-based hotkeys on HP laptops and
|
||||
to read data from WMI such as docking or ambient light sensor state.
|
||||
@ -1171,6 +1194,7 @@ config INTEL_MRFLD_PWRBTN
|
||||
config INTEL_PMC_CORE
|
||||
tristate "Intel PMC Core driver"
|
||||
depends on PCI
|
||||
depends on ACPI
|
||||
help
|
||||
The Intel Platform Controller Hub for Intel Core SoCs provides access
|
||||
to Power Management Controller registers via various interfaces. This
|
||||
@ -1192,7 +1216,7 @@ config INTEL_PMT_CLASS
|
||||
tristate
|
||||
help
|
||||
The Intel Platform Monitoring Technology (PMT) class driver provides
|
||||
the basic sysfs interface and file hierarchy uses by PMT devices.
|
||||
the basic sysfs interface and file hierarchy used by PMT devices.
|
||||
|
||||
For more information, see:
|
||||
<file:Documentation/ABI/testing/sysfs-class-intel_pmt>
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
|
||||
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
|
||||
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
|
||||
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
|
||||
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
|
||||
|
||||
# Acer
|
||||
obj-$(CONFIG_ACERHDF) += acerhdf.o
|
||||
@ -24,6 +25,9 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o
|
||||
# AMD
|
||||
obj-$(CONFIG_AMD_PMC) += amd-pmc.o
|
||||
|
||||
# Advantech
|
||||
obj-$(CONFIG_ADV_SWBUTTON) += adv_swbutton.o
|
||||
|
||||
# Apple
|
||||
obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
|
||||
|
||||
|
121
drivers/platform/x86/adv_swbutton.c
Normal file
121
drivers/platform/x86/adv_swbutton.c
Normal file
@ -0,0 +1,121 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* adv_swbutton.c - Software Button Interface Driver.
|
||||
*
|
||||
* (C) Copyright 2020 Advantech Corporation, Inc
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define ACPI_BUTTON_HID_SWBTN "AHC0310"
|
||||
|
||||
#define ACPI_BUTTON_NOTIFY_SWBTN_RELEASE 0x86
|
||||
#define ACPI_BUTTON_NOTIFY_SWBTN_PRESSED 0x85
|
||||
|
||||
struct adv_swbutton {
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Driver Interface
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
static void adv_swbutton_notify(acpi_handle handle, u32 event, void *context)
|
||||
{
|
||||
struct platform_device *device = context;
|
||||
struct adv_swbutton *button = dev_get_drvdata(&device->dev);
|
||||
|
||||
switch (event) {
|
||||
case ACPI_BUTTON_NOTIFY_SWBTN_RELEASE:
|
||||
input_report_key(button->input, KEY_PROG1, 0);
|
||||
input_sync(button->input);
|
||||
break;
|
||||
case ACPI_BUTTON_NOTIFY_SWBTN_PRESSED:
|
||||
input_report_key(button->input, KEY_PROG1, 1);
|
||||
input_sync(button->input);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&device->dev, "Unsupported event [0x%x]\n", event);
|
||||
}
|
||||
}
|
||||
|
||||
static int adv_swbutton_probe(struct platform_device *device)
|
||||
{
|
||||
struct adv_swbutton *button;
|
||||
struct input_dev *input;
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
acpi_status status;
|
||||
int error;
|
||||
|
||||
button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL);
|
||||
if (!button)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&device->dev, button);
|
||||
|
||||
input = devm_input_allocate_device(&device->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
button->input = input;
|
||||
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", ACPI_BUTTON_HID_SWBTN);
|
||||
|
||||
input->name = "Advantech Software Button";
|
||||
input->phys = button->phys;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &device->dev;
|
||||
set_bit(EV_REP, input->evbit);
|
||||
input_set_capability(input, EV_KEY, KEY_PROG1);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
device_init_wakeup(&device->dev, true);
|
||||
|
||||
status = acpi_install_notify_handler(handle,
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
adv_swbutton_notify,
|
||||
device);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(&device->dev, "Error installing notify handler\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv_swbutton_remove(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY,
|
||||
adv_swbutton_notify);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id button_device_ids[] = {
|
||||
{ACPI_BUTTON_HID_SWBTN, 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, button_device_ids);
|
||||
|
||||
static struct platform_driver adv_swbutton_driver = {
|
||||
.driver = {
|
||||
.name = "adv_swbutton",
|
||||
.acpi_match_table = button_device_ids,
|
||||
},
|
||||
.probe = adv_swbutton_probe,
|
||||
.remove = adv_swbutton_remove,
|
||||
};
|
||||
module_platform_driver(adv_swbutton_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrea Ho");
|
||||
MODULE_DESCRIPTION("Advantech ACPI SW Button Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1569,7 +1569,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct asus_laptop *asus = dev_get_drvdata(dev);
|
||||
acpi_handle handle = asus->handle;
|
||||
bool supported;
|
||||
|
@ -47,6 +47,9 @@ MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>, "
|
||||
MODULE_DESCRIPTION("Asus Generic WMI Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static bool fnlock_default = true;
|
||||
module_param(fnlock_default, bool, 0444);
|
||||
|
||||
#define to_asus_wmi_driver(pdrv) \
|
||||
(container_of((pdrv), struct asus_wmi_driver, platform_driver))
|
||||
|
||||
@ -2673,7 +2676,7 @@ static int asus_wmi_add(struct platform_device *pdev)
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, 2, NULL);
|
||||
|
||||
if (asus_wmi_has_fnlock_key(asus)) {
|
||||
asus->fnlock_locked = true;
|
||||
asus->fnlock_locked = fnlock_default;
|
||||
asus_wmi_fnlock_update(asus);
|
||||
}
|
||||
|
||||
|
@ -956,7 +956,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi)
|
||||
/*
|
||||
* If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
|
||||
* This is OK, however, since all other uses of the device will not
|
||||
* derefence it.
|
||||
* dereference it.
|
||||
*/
|
||||
if (ipml->rf) {
|
||||
retval = rfkill_register(ipml->rf);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Alienware AlienFX control
|
||||
*
|
||||
* Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com>
|
||||
* Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@ -26,7 +26,7 @@
|
||||
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
|
||||
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
|
||||
|
||||
MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
|
||||
MODULE_DESCRIPTION("Alienware special feature control");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
|
||||
|
@ -647,6 +647,6 @@ module_exit(dell_smbios_exit);
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
|
||||
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
|
||||
MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -205,7 +205,7 @@ fail_register:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_smbios_wmi_remove(struct wmi_device *wdev)
|
||||
static void dell_smbios_wmi_remove(struct wmi_device *wdev)
|
||||
{
|
||||
struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
int count;
|
||||
@ -218,7 +218,6 @@ static int dell_smbios_wmi_remove(struct wmi_device *wdev)
|
||||
count = get_order(priv->req_buf_size);
|
||||
free_pages((unsigned long)priv->buf, count);
|
||||
mutex_unlock(&call_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
|
||||
|
@ -174,14 +174,13 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
|
||||
static void dell_wmi_descriptor_remove(struct wmi_device *wdev)
|
||||
{
|
||||
struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
mutex_lock(&list_mutex);
|
||||
list_del(&priv->list);
|
||||
mutex_unlock(&list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
|
||||
@ -201,6 +200,6 @@ static struct wmi_driver dell_wmi_descriptor_driver = {
|
||||
module_wmi_driver(dell_wmi_descriptor_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
|
||||
MODULE_DESCRIPTION("Dell WMI descriptor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -152,12 +152,11 @@ static int bios_attr_set_interface_probe(struct wmi_device *wdev, const void *co
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bios_attr_set_interface_remove(struct wmi_device *wdev)
|
||||
static void bios_attr_set_interface_remove(struct wmi_device *wdev)
|
||||
{
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
wmi_priv.bios_attr_wdev = NULL;
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id bios_attr_set_interface_id_table[] = {
|
||||
|
@ -119,12 +119,11 @@ static int bios_attr_pass_interface_probe(struct wmi_device *wdev, const void *c
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bios_attr_pass_interface_remove(struct wmi_device *wdev)
|
||||
static void bios_attr_pass_interface_remove(struct wmi_device *wdev)
|
||||
{
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
wmi_priv.password_attr_wdev = NULL;
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id bios_attr_pass_interface_id_table[] = {
|
||||
|
@ -399,6 +399,7 @@ static int init_bios_attributes(int attr_type, const char *guid)
|
||||
union acpi_object *obj = NULL;
|
||||
union acpi_object *elements;
|
||||
struct kset *tmp_set;
|
||||
int min_elements;
|
||||
|
||||
/* instance_id needs to be reset for each type GUID
|
||||
* also, instance IDs are unique within GUID but not across
|
||||
@ -409,14 +410,38 @@ static int init_bios_attributes(int attr_type, const char *guid)
|
||||
retval = alloc_attributes_data(attr_type);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
switch (attr_type) {
|
||||
case ENUM: min_elements = 8; break;
|
||||
case INT: min_elements = 9; break;
|
||||
case STR: min_elements = 8; break;
|
||||
case PO: min_elements = 4; break;
|
||||
default:
|
||||
pr_err("Error: Unknown attr_type: %d\n", attr_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* need to use specific instance_id and guid combination to get right data */
|
||||
obj = get_wmiobj_pointer(instance_id, guid);
|
||||
if (!obj || obj->type != ACPI_TYPE_PACKAGE)
|
||||
if (!obj)
|
||||
return -ENODEV;
|
||||
elements = obj->package.elements;
|
||||
|
||||
mutex_lock(&wmi_priv.mutex);
|
||||
while (elements) {
|
||||
while (obj) {
|
||||
if (obj->type != ACPI_TYPE_PACKAGE) {
|
||||
pr_err("Error: Expected ACPI-package type, got: %d\n", obj->type);
|
||||
retval = -EIO;
|
||||
goto err_attr_init;
|
||||
}
|
||||
|
||||
if (obj->package.count < min_elements) {
|
||||
pr_err("Error: ACPI-package does not have enough elements: %d < %d\n",
|
||||
obj->package.count, min_elements);
|
||||
goto nextobj;
|
||||
}
|
||||
|
||||
elements = obj->package.elements;
|
||||
|
||||
/* sanity checking */
|
||||
if (elements[ATTR_NAME].type != ACPI_TYPE_STRING) {
|
||||
pr_debug("incorrect element type\n");
|
||||
@ -481,7 +506,6 @@ nextobj:
|
||||
kfree(obj);
|
||||
instance_id++;
|
||||
obj = get_wmiobj_pointer(instance_id, guid);
|
||||
elements = obj ? obj->package.elements : NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&wmi_priv.mutex);
|
||||
@ -604,7 +628,7 @@ static void __exit sysman_exit(void)
|
||||
module_init(sysman_init);
|
||||
module_exit(sysman_exit);
|
||||
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
|
||||
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
|
||||
MODULE_AUTHOR("Prasanth Ksr <prasanth.ksr@dell.com>");
|
||||
MODULE_AUTHOR("Divya Bharathi <divya.bharathi@dell.com>");
|
||||
MODULE_DESCRIPTION("Dell platform setting control interface");
|
||||
|
@ -714,10 +714,9 @@ static int dell_wmi_probe(struct wmi_device *wdev, const void *context)
|
||||
return dell_wmi_input_setup(wdev);
|
||||
}
|
||||
|
||||
static int dell_wmi_remove(struct wmi_device *wdev)
|
||||
static void dell_wmi_remove(struct wmi_device *wdev)
|
||||
{
|
||||
dell_wmi_input_destroy(wdev);
|
||||
return 0;
|
||||
}
|
||||
static const struct wmi_device_id dell_wmi_id_table[] = {
|
||||
{ .guid_string = DELL_EVENT_GUID },
|
||||
|
203
drivers/platform/x86/gigabyte-wmi.c
Normal file
203
drivers/platform/x86/gigabyte-wmi.c
Normal file
@ -0,0 +1,203 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2021 Thomas Weißschuh <thomas@weissschuh.net>
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#define GIGABYTE_WMI_GUID "DEADBEEF-2001-0000-00A0-C90629100000"
|
||||
#define NUM_TEMPERATURE_SENSORS 6
|
||||
|
||||
static bool force_load;
|
||||
module_param(force_load, bool, 0444);
|
||||
MODULE_PARM_DESC(force_load, "Force loading on unknown platform");
|
||||
|
||||
static u8 usable_sensors_mask;
|
||||
|
||||
enum gigabyte_wmi_commandtype {
|
||||
GIGABYTE_WMI_BUILD_DATE_QUERY = 0x1,
|
||||
GIGABYTE_WMI_MAINBOARD_TYPE_QUERY = 0x2,
|
||||
GIGABYTE_WMI_FIRMWARE_VERSION_QUERY = 0x4,
|
||||
GIGABYTE_WMI_MAINBOARD_NAME_QUERY = 0x5,
|
||||
GIGABYTE_WMI_TEMPERATURE_QUERY = 0x125,
|
||||
};
|
||||
|
||||
struct gigabyte_wmi_args {
|
||||
u32 arg1;
|
||||
};
|
||||
|
||||
static int gigabyte_wmi_perform_query(struct wmi_device *wdev,
|
||||
enum gigabyte_wmi_commandtype command,
|
||||
struct gigabyte_wmi_args *args, struct acpi_buffer *out)
|
||||
{
|
||||
const struct acpi_buffer in = {
|
||||
.length = sizeof(*args),
|
||||
.pointer = args,
|
||||
};
|
||||
|
||||
acpi_status ret = wmidev_evaluate_method(wdev, 0x0, command, &in, out);
|
||||
|
||||
if (ACPI_FAILURE(ret))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gigabyte_wmi_query_integer(struct wmi_device *wdev,
|
||||
enum gigabyte_wmi_commandtype command,
|
||||
struct gigabyte_wmi_args *args, u64 *res)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
int ret;
|
||||
|
||||
ret = gigabyte_wmi_perform_query(wdev, command, args, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
obj = result.pointer;
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
*res = obj->integer.value;
|
||||
else
|
||||
ret = -EIO;
|
||||
kfree(result.pointer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gigabyte_wmi_temperature(struct wmi_device *wdev, u8 sensor, long *res)
|
||||
{
|
||||
struct gigabyte_wmi_args args = {
|
||||
.arg1 = sensor,
|
||||
};
|
||||
u64 temp;
|
||||
acpi_status ret;
|
||||
|
||||
ret = gigabyte_wmi_query_integer(wdev, GIGABYTE_WMI_TEMPERATURE_QUERY, &args, &temp);
|
||||
if (ret == 0) {
|
||||
if (temp == 0)
|
||||
return -ENODEV;
|
||||
*res = (s8)temp * 1000; // value is a signed 8-bit integer
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gigabyte_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct wmi_device *wdev = dev_get_drvdata(dev);
|
||||
|
||||
return gigabyte_wmi_temperature(wdev, channel, val);
|
||||
}
|
||||
|
||||
static umode_t gigabyte_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
return usable_sensors_mask & BIT(channel) ? 0444 : 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *gigabyte_wmi_hwmon_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT,
|
||||
HWMON_T_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops gigabyte_wmi_hwmon_ops = {
|
||||
.read = gigabyte_wmi_hwmon_read,
|
||||
.is_visible = gigabyte_wmi_hwmon_is_visible,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info gigabyte_wmi_hwmon_chip_info = {
|
||||
.ops = &gigabyte_wmi_hwmon_ops,
|
||||
.info = gigabyte_wmi_hwmon_info,
|
||||
};
|
||||
|
||||
static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev)
|
||||
{
|
||||
int i;
|
||||
long temp;
|
||||
u8 r = 0;
|
||||
|
||||
for (i = 0; i < NUM_TEMPERATURE_SENSORS; i++) {
|
||||
if (!gigabyte_wmi_temperature(wdev, i, &temp))
|
||||
r |= BIT(i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id gigabyte_wmi_known_working_platforms[] = {
|
||||
{ .matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "B550 GAMING X V2"),
|
||||
}},
|
||||
{ .matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "B550M AORUS PRO-P"),
|
||||
}},
|
||||
{ .matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "B550M DS3H"),
|
||||
}},
|
||||
{ .matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Z390 I AORUS PRO WIFI-CF"),
|
||||
}},
|
||||
{ .matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "X570 AORUS ELITE"),
|
||||
}},
|
||||
{ .matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "X570 I AORUS PRO WIFI"),
|
||||
}},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int gigabyte_wmi_probe(struct wmi_device *wdev, const void *context)
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
|
||||
if (!dmi_check_system(gigabyte_wmi_known_working_platforms)) {
|
||||
if (!force_load)
|
||||
return -ENODEV;
|
||||
dev_warn(&wdev->dev, "Forcing load on unknown platform");
|
||||
}
|
||||
|
||||
usable_sensors_mask = gigabyte_wmi_detect_sensor_usability(wdev);
|
||||
if (!usable_sensors_mask) {
|
||||
dev_info(&wdev->dev, "No temperature sensors usable");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev, "gigabyte_wmi", wdev,
|
||||
&gigabyte_wmi_hwmon_chip_info, NULL);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct wmi_device_id gigabyte_wmi_id_table[] = {
|
||||
{ GIGABYTE_WMI_GUID, NULL },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct wmi_driver gigabyte_wmi_driver = {
|
||||
.driver = {
|
||||
.name = "gigabyte-wmi",
|
||||
},
|
||||
.id_table = gigabyte_wmi_id_table,
|
||||
.probe = gigabyte_wmi_probe,
|
||||
};
|
||||
module_wmi_driver(gigabyte_wmi_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(wmi, gigabyte_wmi_id_table);
|
||||
MODULE_AUTHOR("Thomas Weißschuh <thomas@weissschuh.net>");
|
||||
MODULE_DESCRIPTION("Gigabyte WMI temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -21,6 +21,7 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/string.h>
|
||||
@ -85,7 +86,7 @@ enum hp_wmi_commandtype {
|
||||
HPWMI_FEATURE2_QUERY = 0x0d,
|
||||
HPWMI_WIRELESS2_QUERY = 0x1b,
|
||||
HPWMI_POSTCODEERROR_QUERY = 0x2a,
|
||||
HPWMI_THERMAL_POLICY_QUERY = 0x4c,
|
||||
HPWMI_THERMAL_PROFILE_QUERY = 0x4c,
|
||||
};
|
||||
|
||||
enum hp_wmi_command {
|
||||
@ -119,6 +120,12 @@ enum hp_wireless2_bits {
|
||||
HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
|
||||
};
|
||||
|
||||
enum hp_thermal_profile {
|
||||
HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
|
||||
HP_THERMAL_PROFILE_DEFAULT = 0x01,
|
||||
HP_THERMAL_PROFILE_COOL = 0x02
|
||||
};
|
||||
|
||||
#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
|
||||
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
|
||||
|
||||
@ -159,6 +166,8 @@ static const struct key_entry hp_wmi_keymap[] = {
|
||||
|
||||
static struct input_dev *hp_wmi_input_dev;
|
||||
static struct platform_device *hp_wmi_platform_dev;
|
||||
static struct platform_profile_handler platform_profile_handler;
|
||||
static bool platform_profile_support;
|
||||
|
||||
static struct rfkill *wifi_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
@ -869,23 +878,98 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int thermal_policy_setup(struct platform_device *device)
|
||||
static int thermal_profile_get(void)
|
||||
{
|
||||
return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY);
|
||||
}
|
||||
|
||||
static int thermal_profile_set(int thermal_profile)
|
||||
{
|
||||
return hp_wmi_perform_query(HPWMI_THERMAL_PROFILE_QUERY, HPWMI_WRITE, &thermal_profile,
|
||||
sizeof(thermal_profile), 0);
|
||||
}
|
||||
|
||||
static int platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
int tp;
|
||||
|
||||
tp = thermal_profile_get();
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
|
||||
switch (tp) {
|
||||
case HP_THERMAL_PROFILE_PERFORMANCE:
|
||||
*profile = PLATFORM_PROFILE_PERFORMANCE;
|
||||
break;
|
||||
case HP_THERMAL_PROFILE_DEFAULT:
|
||||
*profile = PLATFORM_PROFILE_BALANCED;
|
||||
break;
|
||||
case HP_THERMAL_PROFILE_COOL:
|
||||
*profile = PLATFORM_PROFILE_COOL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
tp = hp_wmi_read_int(HPWMI_THERMAL_POLICY_QUERY);
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
tp = HP_THERMAL_PROFILE_PERFORMANCE;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
tp = HP_THERMAL_PROFILE_DEFAULT;
|
||||
break;
|
||||
case PLATFORM_PROFILE_COOL:
|
||||
tp = HP_THERMAL_PROFILE_COOL;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
err = thermal_profile_set(tp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_profile_setup(void)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
tp = thermal_profile_get();
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
|
||||
/*
|
||||
* call thermal policy write command to ensure that the firmware correctly
|
||||
* call thermal profile write command to ensure that the firmware correctly
|
||||
* sets the OEM variables for the DPTF
|
||||
*/
|
||||
err = hp_wmi_perform_query(HPWMI_THERMAL_POLICY_QUERY, HPWMI_WRITE, &tp,
|
||||
sizeof(tp), 0);
|
||||
err = thermal_profile_set(tp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.profile_get = platform_profile_get,
|
||||
platform_profile_handler.profile_set = platform_profile_set,
|
||||
|
||||
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices);
|
||||
|
||||
err = platform_profile_register(&platform_profile_handler);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
platform_profile_support = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -900,7 +984,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||
if (hp_wmi_rfkill_setup(device))
|
||||
hp_wmi_rfkill2_setup(device);
|
||||
|
||||
thermal_policy_setup(device);
|
||||
thermal_profile_setup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -927,6 +1011,9 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
}
|
||||
|
||||
if (platform_profile_support)
|
||||
platform_profile_remove();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -63,9 +63,6 @@ static const struct key_entry intel_vbtn_switchmap[] = {
|
||||
{ KE_END }
|
||||
};
|
||||
|
||||
#define KEYMAP_LEN \
|
||||
(ARRAY_SIZE(intel_vbtn_keymap) + ARRAY_SIZE(intel_vbtn_switchmap) + 1)
|
||||
|
||||
struct intel_vbtn_priv {
|
||||
struct input_dev *buttons_dev;
|
||||
struct input_dev *switches_dev;
|
||||
|
@ -117,10 +117,9 @@ static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
|
||||
static void intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
|
||||
{
|
||||
dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id intel_wmi_sbl_id_table[] = {
|
||||
|
@ -66,11 +66,10 @@ static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_wmi_thunderbolt_remove(struct wmi_device *wdev)
|
||||
static void intel_wmi_thunderbolt_remove(struct wmi_device *wdev)
|
||||
{
|
||||
sysfs_remove_group(&wdev->dev.kobj, &tbt_attribute_group);
|
||||
kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id intel_wmi_thunderbolt_id_table[] = {
|
||||
|
@ -58,7 +58,7 @@ static int chtdc_ti_pwrbtn_probe(struct platform_device *pdev)
|
||||
|
||||
err = devm_request_threaded_irq(dev, irq, NULL,
|
||||
chtdc_ti_pwrbtn_interrupt,
|
||||
0, KBUILD_MODNAME, input);
|
||||
IRQF_ONESHOT, KBUILD_MODNAME, input);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -23,7 +23,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/msr.h>
|
||||
@ -31,7 +33,8 @@
|
||||
|
||||
#include "intel_pmc_core.h"
|
||||
|
||||
static struct pmc_dev pmc;
|
||||
#define ACPI_S0IX_DSM_UUID "57a6512e-3979-4e9d-9708-ff13b2508972"
|
||||
#define ACPI_GET_LOW_MODE_REGISTERS 1
|
||||
|
||||
/* PKGC MSRs are common across Intel Core SoCs */
|
||||
static const struct pmc_bit_map msr_map[] = {
|
||||
@ -380,6 +383,8 @@ static const struct pmc_bit_map cnp_ltr_show_map[] = {
|
||||
* a list of core SoCs using this.
|
||||
*/
|
||||
{"WIGIG", ICL_PMC_LTR_WIGIG},
|
||||
{"THC0", TGL_PMC_LTR_THC0},
|
||||
{"THC1", TGL_PMC_LTR_THC1},
|
||||
/* Below two cannot be used for LTR_IGNORE */
|
||||
{"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT},
|
||||
{"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT},
|
||||
@ -401,6 +406,7 @@ static const struct pmc_reg_map cnp_reg_map = {
|
||||
.pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
|
||||
.pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
|
||||
.ltr_ignore_max = CNP_NUM_IP_IGN_ALLOWED,
|
||||
.etr3_offset = ETR3_OFFSET,
|
||||
};
|
||||
|
||||
static const struct pmc_reg_map icl_reg_map = {
|
||||
@ -418,6 +424,7 @@ static const struct pmc_reg_map icl_reg_map = {
|
||||
.pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
|
||||
.pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
|
||||
.ltr_ignore_max = ICL_NUM_IP_IGN_ALLOWED,
|
||||
.etr3_offset = ETR3_OFFSET,
|
||||
};
|
||||
|
||||
static const struct pmc_bit_map tgl_clocksource_status_map[] = {
|
||||
@ -579,14 +586,65 @@ static const struct pmc_reg_map tgl_reg_map = {
|
||||
.pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
|
||||
.pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
|
||||
.ltr_ignore_max = TGL_NUM_IP_IGN_ALLOWED,
|
||||
.lpm_modes = tgl_lpm_modes,
|
||||
.lpm_num_maps = TGL_LPM_NUM_MAPS,
|
||||
.lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
|
||||
.lpm_sts_latch_en_offset = TGL_LPM_STS_LATCH_EN_OFFSET,
|
||||
.lpm_en_offset = TGL_LPM_EN_OFFSET,
|
||||
.lpm_priority_offset = TGL_LPM_PRI_OFFSET,
|
||||
.lpm_residency_offset = TGL_LPM_RESIDENCY_OFFSET,
|
||||
.lpm_sts = tgl_lpm_maps,
|
||||
.lpm_status_offset = TGL_LPM_STATUS_OFFSET,
|
||||
.lpm_live_status_offset = TGL_LPM_LIVE_STATUS_OFFSET,
|
||||
.etr3_offset = ETR3_OFFSET,
|
||||
};
|
||||
|
||||
static void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
|
||||
const int num_maps = pmcdev->map->lpm_num_maps;
|
||||
u32 lpm_size = LPM_MAX_NUM_MODES * num_maps * 4;
|
||||
union acpi_object *out_obj;
|
||||
struct acpi_device *adev;
|
||||
guid_t s0ix_dsm_guid;
|
||||
u32 *lpm_req_regs, *addr;
|
||||
|
||||
adev = ACPI_COMPANION(&pdev->dev);
|
||||
if (!adev)
|
||||
return;
|
||||
|
||||
guid_parse(ACPI_S0IX_DSM_UUID, &s0ix_dsm_guid);
|
||||
|
||||
out_obj = acpi_evaluate_dsm(adev->handle, &s0ix_dsm_guid, 0,
|
||||
ACPI_GET_LOW_MODE_REGISTERS, NULL);
|
||||
if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
|
||||
u32 size = out_obj->buffer.length;
|
||||
|
||||
if (size != lpm_size) {
|
||||
acpi_handle_debug(adev->handle,
|
||||
"_DSM returned unexpected buffer size, have %u, expect %u\n",
|
||||
size, lpm_size);
|
||||
goto free_acpi_obj;
|
||||
}
|
||||
} else {
|
||||
acpi_handle_debug(adev->handle,
|
||||
"_DSM function 0 evaluation failed\n");
|
||||
goto free_acpi_obj;
|
||||
}
|
||||
|
||||
addr = (u32 *)out_obj->buffer.pointer;
|
||||
|
||||
lpm_req_regs = devm_kzalloc(&pdev->dev, lpm_size * sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
if (!lpm_req_regs)
|
||||
goto free_acpi_obj;
|
||||
|
||||
memcpy(lpm_req_regs, addr, lpm_size);
|
||||
pmcdev->lpm_req_regs = lpm_req_regs;
|
||||
|
||||
free_acpi_obj:
|
||||
ACPI_FREE(out_obj);
|
||||
}
|
||||
|
||||
static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
|
||||
{
|
||||
return readl(pmcdev->regbase + reg_offset);
|
||||
@ -603,6 +661,115 @@ static inline u64 pmc_core_adjust_slp_s0_step(struct pmc_dev *pmcdev, u32 value)
|
||||
return (u64)value * pmcdev->map->slp_s0_res_counter_step;
|
||||
}
|
||||
|
||||
static int set_etr3(struct pmc_dev *pmcdev)
|
||||
{
|
||||
const struct pmc_reg_map *map = pmcdev->map;
|
||||
u32 reg;
|
||||
int err;
|
||||
|
||||
if (!map->etr3_offset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
/* check if CF9 is locked */
|
||||
reg = pmc_core_reg_read(pmcdev, map->etr3_offset);
|
||||
if (reg & ETR3_CF9LOCK) {
|
||||
err = -EACCES;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* write CF9 global reset bit */
|
||||
reg |= ETR3_CF9GR;
|
||||
pmc_core_reg_write(pmcdev, map->etr3_offset, reg);
|
||||
|
||||
reg = pmc_core_reg_read(pmcdev, map->etr3_offset);
|
||||
if (!(reg & ETR3_CF9GR)) {
|
||||
err = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
return err;
|
||||
}
|
||||
static umode_t etr3_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
|
||||
const struct pmc_reg_map *map = pmcdev->map;
|
||||
u32 reg;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
reg = pmc_core_reg_read(pmcdev, map->etr3_offset);
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return reg & ETR3_CF9LOCK ? attr->mode & (SYSFS_PREALLOC | 0444) : attr->mode;
|
||||
}
|
||||
|
||||
static ssize_t etr3_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
|
||||
const struct pmc_reg_map *map = pmcdev->map;
|
||||
u32 reg;
|
||||
|
||||
if (!map->etr3_offset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmcdev, map->etr3_offset);
|
||||
reg &= ETR3_CF9GR | ETR3_CF9LOCK;
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return sysfs_emit(buf, "0x%08x", reg);
|
||||
}
|
||||
|
||||
static ssize_t etr3_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
|
||||
int err;
|
||||
u32 reg;
|
||||
|
||||
err = kstrtouint(buf, 16, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* allow only CF9 writes */
|
||||
if (reg != ETR3_CF9GR)
|
||||
return -EINVAL;
|
||||
|
||||
err = set_etr3(pmcdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_RW(etr3);
|
||||
|
||||
static struct attribute *pmc_attrs[] = {
|
||||
&dev_attr_etr3.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group pmc_attr_group = {
|
||||
.attrs = pmc_attrs,
|
||||
.is_visible = etr3_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *pmc_dev_groups[] = {
|
||||
&pmc_attr_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int pmc_core_dev_state_get(void *data, u64 *val)
|
||||
{
|
||||
struct pmc_dev *pmcdev = data;
|
||||
@ -617,9 +784,8 @@ static int pmc_core_dev_state_get(void *data, u64 *val)
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
|
||||
|
||||
static int pmc_core_check_read_lock_bit(void)
|
||||
static int pmc_core_check_read_lock_bit(struct pmc_dev *pmcdev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = &pmc;
|
||||
u32 value;
|
||||
|
||||
value = pmc_core_reg_read(pmcdev, pmcdev->map->pm_cfg_offset);
|
||||
@ -744,28 +910,26 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused)
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_ppfear);
|
||||
|
||||
/* This function should return link status, 0 means ready */
|
||||
static int pmc_core_mtpmc_link_status(void)
|
||||
static int pmc_core_mtpmc_link_status(struct pmc_dev *pmcdev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = &pmc;
|
||||
u32 value;
|
||||
|
||||
value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET);
|
||||
return value & BIT(SPT_PMC_MSG_FULL_STS_BIT);
|
||||
}
|
||||
|
||||
static int pmc_core_send_msg(u32 *addr_xram)
|
||||
static int pmc_core_send_msg(struct pmc_dev *pmcdev, u32 *addr_xram)
|
||||
{
|
||||
struct pmc_dev *pmcdev = &pmc;
|
||||
u32 dest;
|
||||
int timeout;
|
||||
|
||||
for (timeout = NUM_RETRIES; timeout > 0; timeout--) {
|
||||
if (pmc_core_mtpmc_link_status() == 0)
|
||||
if (pmc_core_mtpmc_link_status(pmcdev) == 0)
|
||||
break;
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
if (timeout <= 0 && pmc_core_mtpmc_link_status())
|
||||
if (timeout <= 0 && pmc_core_mtpmc_link_status(pmcdev))
|
||||
return -EBUSY;
|
||||
|
||||
dest = (*addr_xram & MTPMC_MASK) | (1U << 1);
|
||||
@ -791,7 +955,7 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused)
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
if (pmc_core_send_msg(&mphy_core_reg_low) != 0) {
|
||||
if (pmc_core_send_msg(pmcdev, &mphy_core_reg_low) != 0) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -799,7 +963,7 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused)
|
||||
msleep(10);
|
||||
val_low = pmc_core_reg_read(pmcdev, SPT_PMC_MFPMC_OFFSET);
|
||||
|
||||
if (pmc_core_send_msg(&mphy_core_reg_high) != 0) {
|
||||
if (pmc_core_send_msg(pmcdev, &mphy_core_reg_high) != 0) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -842,7 +1006,7 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused)
|
||||
mphy_common_reg = (SPT_PMC_MPHY_COM_STS_0 << 16);
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
if (pmc_core_send_msg(&mphy_common_reg) != 0) {
|
||||
if (pmc_core_send_msg(pmcdev, &mphy_common_reg) != 0) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -863,9 +1027,8 @@ out_unlock:
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_pll);
|
||||
|
||||
static int pmc_core_send_ltr_ignore(u32 value)
|
||||
static int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value)
|
||||
{
|
||||
struct pmc_dev *pmcdev = &pmc;
|
||||
const struct pmc_reg_map *map = pmcdev->map;
|
||||
u32 reg;
|
||||
int err = 0;
|
||||
@ -891,6 +1054,8 @@ static ssize_t pmc_core_ltr_ignore_write(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
u32 buf_size, value;
|
||||
int err;
|
||||
|
||||
@ -900,7 +1065,7 @@ static ssize_t pmc_core_ltr_ignore_write(struct file *file,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pmc_core_send_ltr_ignore(value);
|
||||
err = pmc_core_send_ltr_ignore(pmcdev, value);
|
||||
|
||||
return err == 0 ? count : err;
|
||||
}
|
||||
@ -1029,21 +1194,26 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr);
|
||||
|
||||
static inline u64 adjust_lpm_residency(struct pmc_dev *pmcdev, u32 offset,
|
||||
const int lpm_adj_x2)
|
||||
{
|
||||
u64 lpm_res = pmc_core_reg_read(pmcdev, offset);
|
||||
|
||||
return GET_X2_COUNTER((u64)lpm_adj_x2 * lpm_res);
|
||||
}
|
||||
|
||||
static int pmc_core_substate_res_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
const char **lpm_modes = pmcdev->map->lpm_modes;
|
||||
const int lpm_adj_x2 = pmcdev->map->lpm_res_counter_step_x2;
|
||||
u32 offset = pmcdev->map->lpm_residency_offset;
|
||||
u32 lpm_en;
|
||||
int index;
|
||||
int i, mode;
|
||||
|
||||
lpm_en = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_en_offset);
|
||||
seq_printf(s, "status substate residency\n");
|
||||
for (index = 0; lpm_modes[index]; index++) {
|
||||
seq_printf(s, "%7s %7s %-15u\n",
|
||||
BIT(index) & lpm_en ? "Enabled" : " ",
|
||||
lpm_modes[index], pmc_core_reg_read(pmcdev, offset));
|
||||
offset += 4;
|
||||
seq_printf(s, "%-10s %-15s\n", "Substate", "Residency");
|
||||
|
||||
pmc_for_each_mode(i, mode, pmcdev) {
|
||||
seq_printf(s, "%-10s %-15llu\n", pmc_lpm_modes[mode],
|
||||
adjust_lpm_residency(pmcdev, offset + (4 * mode), lpm_adj_x2));
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1074,6 +1244,190 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs);
|
||||
|
||||
static void pmc_core_substate_req_header_show(struct seq_file *s)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
int i, mode;
|
||||
|
||||
seq_printf(s, "%30s |", "Element");
|
||||
pmc_for_each_mode(i, mode, pmcdev)
|
||||
seq_printf(s, " %9s |", pmc_lpm_modes[mode]);
|
||||
|
||||
seq_printf(s, " %9s |\n", "Status");
|
||||
}
|
||||
|
||||
static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
const struct pmc_bit_map **maps = pmcdev->map->lpm_sts;
|
||||
const struct pmc_bit_map *map;
|
||||
const int num_maps = pmcdev->map->lpm_num_maps;
|
||||
u32 sts_offset = pmcdev->map->lpm_status_offset;
|
||||
u32 *lpm_req_regs = pmcdev->lpm_req_regs;
|
||||
int mp;
|
||||
|
||||
/* Display the header */
|
||||
pmc_core_substate_req_header_show(s);
|
||||
|
||||
/* Loop over maps */
|
||||
for (mp = 0; mp < num_maps; mp++) {
|
||||
u32 req_mask = 0;
|
||||
u32 lpm_status;
|
||||
int mode, idx, i, len = 32;
|
||||
|
||||
/*
|
||||
* Capture the requirements and create a mask so that we only
|
||||
* show an element if it's required for at least one of the
|
||||
* enabled low power modes
|
||||
*/
|
||||
pmc_for_each_mode(idx, mode, pmcdev)
|
||||
req_mask |= lpm_req_regs[mp + (mode * num_maps)];
|
||||
|
||||
/* Get the last latched status for this map */
|
||||
lpm_status = pmc_core_reg_read(pmcdev, sts_offset + (mp * 4));
|
||||
|
||||
/* Loop over elements in this map */
|
||||
map = maps[mp];
|
||||
for (i = 0; map[i].name && i < len; i++) {
|
||||
u32 bit_mask = map[i].bit_mask;
|
||||
|
||||
if (!(bit_mask & req_mask))
|
||||
/*
|
||||
* Not required for any enabled states
|
||||
* so don't display
|
||||
*/
|
||||
continue;
|
||||
|
||||
/* Display the element name in the first column */
|
||||
seq_printf(s, "%30s |", map[i].name);
|
||||
|
||||
/* Loop over the enabled states and display if required */
|
||||
pmc_for_each_mode(idx, mode, pmcdev) {
|
||||
if (lpm_req_regs[mp + (mode * num_maps)] & bit_mask)
|
||||
seq_printf(s, " %9s |",
|
||||
"Required");
|
||||
else
|
||||
seq_printf(s, " %9s |", " ");
|
||||
}
|
||||
|
||||
/* In Status column, show the last captured state of this agent */
|
||||
if (lpm_status & bit_mask)
|
||||
seq_printf(s, " %9s |", "Yes");
|
||||
else
|
||||
seq_printf(s, " %9s |", " ");
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs);
|
||||
|
||||
static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
bool c10;
|
||||
u32 reg;
|
||||
int idx, mode;
|
||||
|
||||
reg = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_sts_latch_en_offset);
|
||||
if (reg & LPM_STS_LATCH_MODE) {
|
||||
seq_puts(s, "c10");
|
||||
c10 = false;
|
||||
} else {
|
||||
seq_puts(s, "[c10]");
|
||||
c10 = true;
|
||||
}
|
||||
|
||||
pmc_for_each_mode(idx, mode, pmcdev) {
|
||||
if ((BIT(mode) & reg) && !c10)
|
||||
seq_printf(s, " [%s]", pmc_lpm_modes[mode]);
|
||||
else
|
||||
seq_printf(s, " %s", pmc_lpm_modes[mode]);
|
||||
}
|
||||
|
||||
seq_puts(s, " clear\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
bool clear = false, c10 = false;
|
||||
unsigned char buf[8];
|
||||
int idx, m, mode;
|
||||
u32 reg;
|
||||
|
||||
if (count > sizeof(buf) - 1)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
buf[count] = '\0';
|
||||
|
||||
/*
|
||||
* Allowed strings are:
|
||||
* Any enabled substate, e.g. 'S0i2.0'
|
||||
* 'c10'
|
||||
* 'clear'
|
||||
*/
|
||||
mode = sysfs_match_string(pmc_lpm_modes, buf);
|
||||
|
||||
/* Check string matches enabled mode */
|
||||
pmc_for_each_mode(idx, m, pmcdev)
|
||||
if (mode == m)
|
||||
break;
|
||||
|
||||
if (mode != m || mode < 0) {
|
||||
if (sysfs_streq(buf, "clear"))
|
||||
clear = true;
|
||||
else if (sysfs_streq(buf, "c10"))
|
||||
c10 = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmcdev, pmcdev->map->etr3_offset);
|
||||
reg |= ETR3_CLEAR_LPM_EVENTS;
|
||||
pmc_core_reg_write(pmcdev, pmcdev->map->etr3_offset, reg);
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
if (c10) {
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_sts_latch_en_offset);
|
||||
reg &= ~LPM_STS_LATCH_MODE;
|
||||
pmc_core_reg_write(pmcdev, pmcdev->map->lpm_sts_latch_en_offset, reg);
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* For LPM mode latching we set the latch enable bit and selected mode
|
||||
* and clear everything else.
|
||||
*/
|
||||
reg = LPM_STS_LATCH_MODE | BIT(mode);
|
||||
mutex_lock(&pmcdev->lock);
|
||||
pmc_core_reg_write(pmcdev, pmcdev->map->lpm_sts_latch_en_offset, reg);
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
DEFINE_PMC_CORE_ATTR_WRITE(pmc_core_lpm_latch_mode);
|
||||
|
||||
static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
@ -1095,6 +1449,45 @@ static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc);
|
||||
|
||||
static void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev)
|
||||
{
|
||||
u8 lpm_priority[LPM_MAX_NUM_MODES];
|
||||
u32 lpm_en;
|
||||
int mode, i, p;
|
||||
|
||||
/* Use LPM Maps to indicate support for substates */
|
||||
if (!pmcdev->map->lpm_num_maps)
|
||||
return;
|
||||
|
||||
lpm_en = pmc_core_reg_read(pmcdev, pmcdev->map->lpm_en_offset);
|
||||
pmcdev->num_lpm_modes = hweight32(lpm_en);
|
||||
|
||||
/* Each byte contains information for 2 modes (7:4 and 3:0) */
|
||||
for (mode = 0; mode < LPM_MAX_NUM_MODES; mode += 2) {
|
||||
u8 priority = pmc_core_reg_read_byte(pmcdev,
|
||||
pmcdev->map->lpm_priority_offset + (mode / 2));
|
||||
int pri0 = GENMASK(3, 0) & priority;
|
||||
int pri1 = (GENMASK(7, 4) & priority) >> 4;
|
||||
|
||||
lpm_priority[pri0] = mode;
|
||||
lpm_priority[pri1] = mode + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop though all modes from lowest to highest priority,
|
||||
* and capture all enabled modes in order
|
||||
*/
|
||||
i = 0;
|
||||
for (p = LPM_MAX_NUM_MODES - 1; p >= 0; p--) {
|
||||
int mode = lpm_priority[p];
|
||||
|
||||
if (!(BIT(mode) & lpm_en))
|
||||
continue;
|
||||
|
||||
pmcdev->lpm_en_modes[i++] = mode;
|
||||
}
|
||||
}
|
||||
|
||||
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
|
||||
{
|
||||
debugfs_remove_recursive(pmcdev->dbgfs_dir);
|
||||
@ -1153,6 +1546,15 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
||||
debugfs_create_file("substate_live_status_registers", 0444,
|
||||
pmcdev->dbgfs_dir, pmcdev,
|
||||
&pmc_core_substate_l_sts_regs_fops);
|
||||
debugfs_create_file("lpm_latch_mode", 0644,
|
||||
pmcdev->dbgfs_dir, pmcdev,
|
||||
&pmc_core_lpm_latch_mode_fops);
|
||||
}
|
||||
|
||||
if (pmcdev->lpm_req_regs) {
|
||||
debugfs_create_file("substate_requirements", 0444,
|
||||
pmcdev->dbgfs_dir, pmcdev,
|
||||
&pmc_core_substate_req_regs_fops);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1171,6 +1573,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, &tgl_reg_map),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, &icl_reg_map),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &tgl_reg_map),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &tgl_reg_map),
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1186,9 +1589,15 @@ static const struct pci_device_id pmc_pci_ids[] = {
|
||||
* the platform BIOS enforces 24Mhz crystal to shutdown
|
||||
* before PMC can assert SLP_S0#.
|
||||
*/
|
||||
static bool xtal_ignore;
|
||||
static int quirk_xtal_ignore(const struct dmi_system_id *id)
|
||||
{
|
||||
struct pmc_dev *pmcdev = &pmc;
|
||||
xtal_ignore = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pmc_core_xtal_ignore(struct pmc_dev *pmcdev)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = pmc_core_reg_read(pmcdev, pmcdev->map->pm_vric1_offset);
|
||||
@ -1197,7 +1606,6 @@ static int quirk_xtal_ignore(const struct dmi_system_id *id)
|
||||
/* Low Voltage Mode Enable */
|
||||
value &= ~SPT_PMC_VRIC1_SLPS0LVEN;
|
||||
pmc_core_reg_write(pmcdev, pmcdev->map->pm_vric1_offset, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id pmc_core_dmi_table[] = {
|
||||
@ -1212,16 +1620,30 @@ static const struct dmi_system_id pmc_core_dmi_table[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static void pmc_core_do_dmi_quirks(struct pmc_dev *pmcdev)
|
||||
{
|
||||
dmi_check_system(pmc_core_dmi_table);
|
||||
|
||||
if (xtal_ignore)
|
||||
pmc_core_xtal_ignore(pmcdev);
|
||||
}
|
||||
|
||||
static int pmc_core_probe(struct platform_device *pdev)
|
||||
{
|
||||
static bool device_initialized;
|
||||
struct pmc_dev *pmcdev = &pmc;
|
||||
struct pmc_dev *pmcdev;
|
||||
const struct x86_cpu_id *cpu_id;
|
||||
u64 slp_s0_addr;
|
||||
|
||||
if (device_initialized)
|
||||
return -ENODEV;
|
||||
|
||||
pmcdev = devm_kzalloc(&pdev->dev, sizeof(*pmcdev), GFP_KERNEL);
|
||||
if (!pmcdev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, pmcdev);
|
||||
|
||||
cpu_id = x86_match_cpu(intel_pmc_core_ids);
|
||||
if (!cpu_id)
|
||||
return -ENODEV;
|
||||
@ -1251,9 +1673,13 @@ static int pmc_core_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&pmcdev->lock);
|
||||
platform_set_drvdata(pdev, pmcdev);
|
||||
pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit();
|
||||
dmi_check_system(pmc_core_dmi_table);
|
||||
|
||||
pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(pmcdev);
|
||||
pmc_core_get_low_power_modes(pmcdev);
|
||||
pmc_core_do_dmi_quirks(pmcdev);
|
||||
|
||||
if (pmcdev->map == &tgl_reg_map)
|
||||
pmc_core_get_tgl_lpm_reqs(pdev);
|
||||
|
||||
/*
|
||||
* On TGL, due to a hardware limitation, the GBE LTR blocks PC10 when
|
||||
@ -1261,7 +1687,7 @@ static int pmc_core_probe(struct platform_device *pdev)
|
||||
*/
|
||||
if (pmcdev->map == &tgl_reg_map) {
|
||||
dev_dbg(&pdev->dev, "ignoring GBE LTR\n");
|
||||
pmc_core_send_ltr_ignore(3);
|
||||
pmc_core_send_ltr_ignore(pmcdev, 3);
|
||||
}
|
||||
|
||||
pmc_core_dbgfs_register(pmcdev);
|
||||
@ -1384,6 +1810,7 @@ static struct platform_driver pmc_core_driver = {
|
||||
.name = "intel_pmc_core",
|
||||
.acpi_match_table = ACPI_PTR(pmc_core_acpi_ids),
|
||||
.pm = &pmc_core_pm_ops,
|
||||
.dev_groups = pmc_dev_groups,
|
||||
},
|
||||
.probe = pmc_core_probe,
|
||||
.remove = pmc_core_remove,
|
||||
|
@ -187,20 +187,38 @@ enum ppfear_regs {
|
||||
#define ICL_PMC_LTR_WIGIG 0x1BFC
|
||||
#define ICL_PMC_SLP_S0_RES_COUNTER_STEP 0x64
|
||||
|
||||
#define TGL_NUM_IP_IGN_ALLOWED 22
|
||||
#define LPM_MAX_NUM_MODES 8
|
||||
#define GET_X2_COUNTER(v) ((v) >> 1)
|
||||
#define LPM_STS_LATCH_MODE BIT(31)
|
||||
|
||||
#define TGL_PMC_SLP_S0_RES_COUNTER_STEP 0x7A
|
||||
#define TGL_PMC_LTR_THC0 0x1C04
|
||||
#define TGL_PMC_LTR_THC1 0x1C08
|
||||
#define TGL_NUM_IP_IGN_ALLOWED 23
|
||||
#define TGL_PMC_LPM_RES_COUNTER_STEP_X2 61 /* 30.5us * 2 */
|
||||
|
||||
/*
|
||||
* Tigerlake Power Management Controller register offsets
|
||||
*/
|
||||
#define TGL_LPM_STS_LATCH_EN_OFFSET 0x1C34
|
||||
#define TGL_LPM_EN_OFFSET 0x1C78
|
||||
#define TGL_LPM_RESIDENCY_OFFSET 0x1C80
|
||||
|
||||
/* Tigerlake Low Power Mode debug registers */
|
||||
#define TGL_LPM_STATUS_OFFSET 0x1C3C
|
||||
#define TGL_LPM_LIVE_STATUS_OFFSET 0x1C5C
|
||||
#define TGL_LPM_PRI_OFFSET 0x1C7C
|
||||
#define TGL_LPM_NUM_MAPS 6
|
||||
|
||||
const char *tgl_lpm_modes[] = {
|
||||
/* Extended Test Mode Register 3 (CNL and later) */
|
||||
#define ETR3_OFFSET 0x1048
|
||||
#define ETR3_CF9GR BIT(20)
|
||||
#define ETR3_CF9LOCK BIT(31)
|
||||
|
||||
/* Extended Test Mode Register LPM bits (TGL and later */
|
||||
#define ETR3_CLEAR_LPM_EVENTS BIT(28)
|
||||
|
||||
const char *pmc_lpm_modes[] = {
|
||||
"S0i2.0",
|
||||
"S0i2.1",
|
||||
"S0i2.2",
|
||||
@ -258,11 +276,15 @@ struct pmc_reg_map {
|
||||
const u32 ltr_ignore_max;
|
||||
const u32 pm_vric1_offset;
|
||||
/* Low Power Mode registers */
|
||||
const char **lpm_modes;
|
||||
const int lpm_num_maps;
|
||||
const int lpm_res_counter_step_x2;
|
||||
const u32 lpm_sts_latch_en_offset;
|
||||
const u32 lpm_en_offset;
|
||||
const u32 lpm_priority_offset;
|
||||
const u32 lpm_residency_offset;
|
||||
const u32 lpm_status_offset;
|
||||
const u32 lpm_live_status_offset;
|
||||
const u32 etr3_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -278,6 +300,9 @@ struct pmc_reg_map {
|
||||
* @check_counters: On resume, check if counters are getting incremented
|
||||
* @pc10_counter: PC10 residency counter
|
||||
* @s0ix_counter: S0ix residency (step adjusted)
|
||||
* @num_lpm_modes: Count of enabled modes
|
||||
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
|
||||
* @lpm_req_regs: List of substate requirements
|
||||
*
|
||||
* pmc_dev contains info about power management controller device.
|
||||
*/
|
||||
@ -292,6 +317,28 @@ struct pmc_dev {
|
||||
bool check_counters; /* Check for counter increments on resume */
|
||||
u64 pc10_counter;
|
||||
u64 s0ix_counter;
|
||||
int num_lpm_modes;
|
||||
int lpm_en_modes[LPM_MAX_NUM_MODES];
|
||||
u32 *lpm_req_regs;
|
||||
};
|
||||
|
||||
#define pmc_for_each_mode(i, mode, pmcdev) \
|
||||
for (i = 0, mode = pmcdev->lpm_en_modes[i]; \
|
||||
i < pmcdev->num_lpm_modes; \
|
||||
i++, mode = pmcdev->lpm_en_modes[i])
|
||||
|
||||
#define DEFINE_PMC_CORE_ATTR_WRITE(__name) \
|
||||
static int __name ## _open(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
return single_open(file, __name ## _show, inode->i_private); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations __name ## _fops = { \
|
||||
.owner = THIS_MODULE, \
|
||||
.open = __name ## _open, \
|
||||
.read = seq_read, \
|
||||
.write = __name ## _write, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
#endif /* PMC_CORE_H */
|
||||
|
@ -19,6 +19,28 @@
|
||||
#define PMT_XA_MAX INT_MAX
|
||||
#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
|
||||
|
||||
/*
|
||||
* Early implementations of PMT on client platforms have some
|
||||
* differences from the server platforms (which use the Out Of Band
|
||||
* Management Services Module OOBMSM). This list tracks those
|
||||
* platforms as needed to handle those differences. Newer client
|
||||
* platforms are expected to be fully compatible with server.
|
||||
*/
|
||||
static const struct pci_device_id pmt_telem_early_client_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x467d) }, /* ADL */
|
||||
{ PCI_VDEVICE(INTEL, 0x490e) }, /* DG1 */
|
||||
{ PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */
|
||||
{ }
|
||||
};
|
||||
|
||||
bool intel_pmt_is_early_client_hw(struct device *dev)
|
||||
{
|
||||
struct pci_dev *parent = to_pci_dev(dev->parent);
|
||||
|
||||
return !!pci_match_id(pmt_telem_early_client_pci_ids, parent);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw);
|
||||
|
||||
/*
|
||||
* sysfs
|
||||
*/
|
||||
@ -147,6 +169,30 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry,
|
||||
* base address = end of discovery region + base offset
|
||||
*/
|
||||
entry->base_addr = disc_res->end + 1 + header->base_offset;
|
||||
|
||||
/*
|
||||
* Some hardware use a different calculation for the base address
|
||||
* when access_type == ACCESS_LOCAL. On the these systems
|
||||
* ACCCESS_LOCAL refers to an address in the same BAR as the
|
||||
* header but at a fixed offset. But as the header address was
|
||||
* supplied to the driver, we don't know which BAR it was in.
|
||||
* So search for the bar whose range includes the header address.
|
||||
*/
|
||||
if (intel_pmt_is_early_client_hw(dev)) {
|
||||
int i;
|
||||
|
||||
entry->base_addr = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
if (disc_res->start >= pci_resource_start(pci_dev, i) &&
|
||||
(disc_res->start <= pci_resource_end(pci_dev, i))) {
|
||||
entry->base_addr = pci_resource_start(pci_dev, i) +
|
||||
header->base_offset;
|
||||
break;
|
||||
}
|
||||
if (!entry->base_addr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
case ACCESS_BARID:
|
||||
/*
|
||||
|
@ -44,6 +44,7 @@ struct intel_pmt_namespace {
|
||||
struct device *dev);
|
||||
};
|
||||
|
||||
bool intel_pmt_is_early_client_hw(struct device *dev);
|
||||
int intel_pmt_dev_create(struct intel_pmt_entry *entry,
|
||||
struct intel_pmt_namespace *ns,
|
||||
struct platform_device *pdev, int idx);
|
||||
|
@ -34,26 +34,6 @@ struct pmt_telem_priv {
|
||||
struct intel_pmt_entry entry[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Early implementations of PMT on client platforms have some
|
||||
* differences from the server platforms (which use the Out Of Band
|
||||
* Management Services Module OOBMSM). This list tracks those
|
||||
* platforms as needed to handle those differences. Newer client
|
||||
* platforms are expected to be fully compatible with server.
|
||||
*/
|
||||
static const struct pci_device_id pmt_telem_early_client_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */
|
||||
{ PCI_VDEVICE(INTEL, 0x467d) }, /* ADL */
|
||||
{ }
|
||||
};
|
||||
|
||||
static bool intel_pmt_is_early_client_hw(struct device *dev)
|
||||
{
|
||||
struct pci_dev *parent = to_pci_dev(dev->parent);
|
||||
|
||||
return !!pci_match_id(pmt_telem_early_client_pci_ids, parent);
|
||||
}
|
||||
|
||||
static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
|
||||
struct device *dev)
|
||||
{
|
||||
|
@ -21,12 +21,16 @@
|
||||
#define PUNIT_MAILBOX_BUSY_BIT 31
|
||||
|
||||
/*
|
||||
* The average time to complete some commands is about 40us. The current
|
||||
* count is enough to satisfy 40us. But when the firmware is very busy, this
|
||||
* causes timeout occasionally. So increase to deal with some worst case
|
||||
* scenarios. Most of the command still complete in few us.
|
||||
* The average time to complete mailbox commands is less than 40us. Most of
|
||||
* the commands complete in few micro seconds. But the same firmware handles
|
||||
* requests from all power management features.
|
||||
* We can create a scenario where we flood the firmware with requests then
|
||||
* the mailbox response can be delayed for 100s of micro seconds. So define
|
||||
* two timeouts. One for average case and one for long.
|
||||
* If the firmware is taking more than average, just call cond_resched().
|
||||
*/
|
||||
#define OS_MAILBOX_RETRY_COUNT 100
|
||||
#define OS_MAILBOX_TIMEOUT_AVG_US 40
|
||||
#define OS_MAILBOX_TIMEOUT_MAX_US 1000
|
||||
|
||||
struct isst_if_device {
|
||||
struct mutex mutex;
|
||||
@ -35,11 +39,13 @@ struct isst_if_device {
|
||||
static int isst_if_mbox_cmd(struct pci_dev *pdev,
|
||||
struct isst_if_mbox_cmd *mbox_cmd)
|
||||
{
|
||||
u32 retries, data;
|
||||
s64 tm_delta = 0;
|
||||
ktime_t tm;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
/* Poll for rb bit == 0 */
|
||||
retries = OS_MAILBOX_RETRY_COUNT;
|
||||
tm = ktime_get();
|
||||
do {
|
||||
ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
|
||||
&data);
|
||||
@ -48,11 +54,14 @@ static int isst_if_mbox_cmd(struct pci_dev *pdev,
|
||||
|
||||
if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
|
||||
ret = -EBUSY;
|
||||
tm_delta = ktime_us_delta(ktime_get(), tm);
|
||||
if (tm_delta > OS_MAILBOX_TIMEOUT_AVG_US)
|
||||
cond_resched();
|
||||
continue;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
} while (--retries);
|
||||
} while (tm_delta < OS_MAILBOX_TIMEOUT_MAX_US);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -74,7 +83,8 @@ static int isst_if_mbox_cmd(struct pci_dev *pdev,
|
||||
return ret;
|
||||
|
||||
/* Poll for rb bit == 0 */
|
||||
retries = OS_MAILBOX_RETRY_COUNT;
|
||||
tm_delta = 0;
|
||||
tm = ktime_get();
|
||||
do {
|
||||
ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
|
||||
&data);
|
||||
@ -83,6 +93,9 @@ static int isst_if_mbox_cmd(struct pci_dev *pdev,
|
||||
|
||||
if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
|
||||
ret = -EBUSY;
|
||||
tm_delta = ktime_us_delta(ktime_get(), tm);
|
||||
if (tm_delta > OS_MAILBOX_TIMEOUT_AVG_US)
|
||||
cond_resched();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -96,7 +109,7 @@ static int isst_if_mbox_cmd(struct pci_dev *pdev,
|
||||
mbox_cmd->resp_data = data;
|
||||
ret = 0;
|
||||
break;
|
||||
} while (--retries);
|
||||
} while (tm_delta < OS_MAILBOX_TIMEOUT_MAX_US);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -678,7 +678,7 @@ static int __init acpi_init(void)
|
||||
|
||||
result = acpi_bus_register_driver(&acpi_driver);
|
||||
if (result < 0) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error registering driver\n"));
|
||||
pr_debug("Error registering driver\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -973,7 +973,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
|
||||
pcc->mute = pcc->sinf[SINF_MUTE];
|
||||
pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
|
||||
pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT];
|
||||
result = pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];
|
||||
pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];
|
||||
|
||||
/* add sysfs attributes */
|
||||
result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
|
||||
|
@ -393,34 +393,10 @@ static const struct dmi_system_id critclk_systems[] = {
|
||||
},
|
||||
{
|
||||
/* pmc_plt_clk* - are used for ethernet controllers */
|
||||
.ident = "Beckhoff CB3163",
|
||||
.ident = "Beckhoff Baytrail",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "CB3163"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* pmc_plt_clk* - are used for ethernet controllers */
|
||||
.ident = "Beckhoff CB4063",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "CB4063"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* pmc_plt_clk* - are used for ethernet controllers */
|
||||
.ident = "Beckhoff CB6263",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "CB6263"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* pmc_plt_clk* - are used for ethernet controllers */
|
||||
.ident = "Beckhoff CB6363",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "CB6363"),
|
||||
DMI_MATCH(DMI_PRODUCT_FAMILY, "CBxx63"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -175,6 +175,12 @@ enum tpacpi_hkey_event_t {
|
||||
or port replicator */
|
||||
TP_HKEY_EV_HOTPLUG_UNDOCK = 0x4011, /* undocked from hotplug
|
||||
dock or port replicator */
|
||||
/*
|
||||
* Thinkpad X1 Tablet series devices emit 0x4012 and 0x4013
|
||||
* when keyboard cover is attached, detached or folded onto the back
|
||||
*/
|
||||
TP_HKEY_EV_KBD_COVER_ATTACH = 0x4012, /* keyboard cover attached */
|
||||
TP_HKEY_EV_KBD_COVER_DETACH = 0x4013, /* keyboard cover detached or folded back */
|
||||
|
||||
/* User-interface events */
|
||||
TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */
|
||||
@ -3991,6 +3997,23 @@ static bool hotkey_notify_dockevent(const u32 hkey,
|
||||
pr_info("undocked from hotplug port replicator\n");
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Deliberately ignore attaching and detaching the keybord cover to avoid
|
||||
* duplicates from intel-vbtn, which already emits SW_TABLET_MODE events
|
||||
* to userspace.
|
||||
*
|
||||
* Please refer to the following thread for more information and a preliminary
|
||||
* implementation using the GTOP ("Get Tablet OPtions") interface that could be
|
||||
* extended to other attachment options of the ThinkPad X1 Tablet series, such as
|
||||
* the Pico cartridge dock module:
|
||||
* https://lore.kernel.org/platform-driver-x86/38cb8265-1e30-d547-9e12-b4ae290be737@a-kobel.de/
|
||||
*/
|
||||
case TP_HKEY_EV_KBD_COVER_ATTACH:
|
||||
case TP_HKEY_EV_KBD_COVER_DETACH:
|
||||
*send_acpi_ev = false;
|
||||
*ignore_acpi_ev = true;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -4088,7 +4111,7 @@ static bool hotkey_notify_6xxx(const u32 hkey,
|
||||
return true;
|
||||
|
||||
case TP_HKEY_EV_KEY_FN_ESC:
|
||||
/* Get the media key status to foce the status LED to update */
|
||||
/* Get the media key status to force the status LED to update */
|
||||
acpi_evalf(hkey_handle, NULL, "GMKS", "v");
|
||||
*send_acpi_ev = false;
|
||||
*ignore_acpi_ev = true;
|
||||
@ -6260,6 +6283,7 @@ enum thermal_access_mode {
|
||||
enum { /* TPACPI_THERMAL_TPEC_* */
|
||||
TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
|
||||
TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
|
||||
TP_EC_FUNCREV = 0xEF, /* ACPI EC Functional revision */
|
||||
TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
|
||||
|
||||
TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */
|
||||
@ -6272,6 +6296,8 @@ struct ibm_thermal_sensors_struct {
|
||||
};
|
||||
|
||||
static enum thermal_access_mode thermal_read_mode;
|
||||
static const struct attribute_group *thermal_attr_group;
|
||||
static bool thermal_use_labels;
|
||||
|
||||
/* idx is zero-based */
|
||||
static int thermal_get_sensor(int idx, s32 *value)
|
||||
@ -6454,11 +6480,33 @@ static const struct attribute_group thermal_temp_input8_group = {
|
||||
#undef THERMAL_SENSOR_ATTR_TEMP
|
||||
#undef THERMAL_ATTRS
|
||||
|
||||
static ssize_t temp1_label_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "CPU\n");
|
||||
}
|
||||
static DEVICE_ATTR_RO(temp1_label);
|
||||
|
||||
static ssize_t temp2_label_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "GPU\n");
|
||||
}
|
||||
static DEVICE_ATTR_RO(temp2_label);
|
||||
|
||||
static struct attribute *temp_label_attributes[] = {
|
||||
&dev_attr_temp1_label.attr,
|
||||
&dev_attr_temp2_label.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group temp_label_attr_group = {
|
||||
.attrs = temp_label_attributes,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static int __init thermal_init(struct ibm_init_struct *iibm)
|
||||
{
|
||||
u8 t, ta1, ta2;
|
||||
u8 t, ta1, ta2, ver = 0;
|
||||
int i;
|
||||
int acpi_tmp7;
|
||||
int res;
|
||||
@ -6473,7 +6521,14 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
|
||||
* 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
|
||||
* non-implemented, thermal sensors return 0x80 when
|
||||
* not available
|
||||
* The above rule is unfortunately flawed. This has been seen with
|
||||
* 0xC2 (power supply ID) causing thermal control problems.
|
||||
* The EC version can be determined by offset 0xEF and at least for
|
||||
* version 3 the Lenovo firmware team confirmed that registers 0xC0-0xC7
|
||||
* are not thermal registers.
|
||||
*/
|
||||
if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
|
||||
pr_warn("Thinkpad ACPI EC unable to access EC version\n");
|
||||
|
||||
ta1 = ta2 = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
@ -6483,11 +6538,13 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
|
||||
ta1 = 0;
|
||||
break;
|
||||
}
|
||||
if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
|
||||
ta2 |= t;
|
||||
} else {
|
||||
ta1 = 0;
|
||||
break;
|
||||
if (ver < 3) {
|
||||
if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
|
||||
ta2 |= t;
|
||||
} else {
|
||||
ta1 = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ta1 == 0) {
|
||||
@ -6500,9 +6557,14 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
|
||||
thermal_read_mode = TPACPI_THERMAL_NONE;
|
||||
}
|
||||
} else {
|
||||
thermal_read_mode =
|
||||
(ta2 != 0) ?
|
||||
TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
|
||||
if (ver >= 3) {
|
||||
thermal_read_mode = TPACPI_THERMAL_TPEC_8;
|
||||
thermal_use_labels = true;
|
||||
} else {
|
||||
thermal_read_mode =
|
||||
(ta2 != 0) ?
|
||||
TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
|
||||
}
|
||||
}
|
||||
} else if (acpi_tmp7) {
|
||||
if (tpacpi_is_ibm() &&
|
||||
@ -6524,44 +6586,40 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
|
||||
|
||||
switch (thermal_read_mode) {
|
||||
case TPACPI_THERMAL_TPEC_16:
|
||||
res = sysfs_create_group(&tpacpi_hwmon->kobj,
|
||||
&thermal_temp_input16_group);
|
||||
if (res)
|
||||
return res;
|
||||
thermal_attr_group = &thermal_temp_input16_group;
|
||||
break;
|
||||
case TPACPI_THERMAL_TPEC_8:
|
||||
case TPACPI_THERMAL_ACPI_TMP07:
|
||||
case TPACPI_THERMAL_ACPI_UPDT:
|
||||
res = sysfs_create_group(&tpacpi_hwmon->kobj,
|
||||
&thermal_temp_input8_group);
|
||||
if (res)
|
||||
return res;
|
||||
thermal_attr_group = &thermal_temp_input8_group;
|
||||
break;
|
||||
case TPACPI_THERMAL_NONE:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
res = sysfs_create_group(&tpacpi_hwmon->kobj, thermal_attr_group);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (thermal_use_labels) {
|
||||
res = sysfs_create_group(&tpacpi_hwmon->kobj, &temp_label_attr_group);
|
||||
if (res) {
|
||||
sysfs_remove_group(&tpacpi_hwmon->kobj, thermal_attr_group);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void thermal_exit(void)
|
||||
{
|
||||
switch (thermal_read_mode) {
|
||||
case TPACPI_THERMAL_TPEC_16:
|
||||
sysfs_remove_group(&tpacpi_hwmon->kobj,
|
||||
&thermal_temp_input16_group);
|
||||
break;
|
||||
case TPACPI_THERMAL_TPEC_8:
|
||||
case TPACPI_THERMAL_ACPI_TMP07:
|
||||
case TPACPI_THERMAL_ACPI_UPDT:
|
||||
sysfs_remove_group(&tpacpi_hwmon->kobj,
|
||||
&thermal_temp_input8_group);
|
||||
break;
|
||||
case TPACPI_THERMAL_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (thermal_attr_group)
|
||||
sysfs_remove_group(&tpacpi_hwmon->kobj, thermal_attr_group);
|
||||
|
||||
if (thermal_use_labels)
|
||||
sysfs_remove_group(&tpacpi_hwmon->kobj, &temp_label_attr_group);
|
||||
}
|
||||
|
||||
static int thermal_read(struct seq_file *m)
|
||||
@ -10050,6 +10108,7 @@ static struct ibm_struct proxsensor_driver_data = {
|
||||
*/
|
||||
|
||||
#define DYTC_CMD_SET 1 /* To enable/disable IC function mode */
|
||||
#define DYTC_CMD_MMC_GET 8 /* To get current MMC function and mode */
|
||||
#define DYTC_CMD_RESET 0x1ff /* To reset back to default */
|
||||
|
||||
#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */
|
||||
@ -10066,6 +10125,10 @@ static struct ibm_struct proxsensor_driver_data = {
|
||||
#define DYTC_MODE_PERFORM 2 /* High power mode aka performance */
|
||||
#define DYTC_MODE_LOWPOWER 3 /* Low power mode */
|
||||
#define DYTC_MODE_BALANCE 0xF /* Default mode aka balanced */
|
||||
#define DYTC_MODE_MMC_BALANCE 0 /* Default mode from MMC_GET, aka balanced */
|
||||
|
||||
#define DYTC_ERR_MASK 0xF /* Bits 0-3 in cmd result are the error result */
|
||||
#define DYTC_ERR_SUCCESS 1 /* CMD completed successful */
|
||||
|
||||
#define DYTC_SET_COMMAND(function, mode, on) \
|
||||
(DYTC_CMD_SET | (function) << DYTC_SET_FUNCTION_BIT | \
|
||||
@ -10080,6 +10143,7 @@ static bool dytc_profile_available;
|
||||
static enum platform_profile_option dytc_current_profile;
|
||||
static atomic_t dytc_ignore_event = ATOMIC_INIT(0);
|
||||
static DEFINE_MUTEX(dytc_mutex);
|
||||
static bool dytc_mmc_get_available;
|
||||
|
||||
static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)
|
||||
{
|
||||
@ -10088,6 +10152,7 @@ static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *p
|
||||
*profile = PLATFORM_PROFILE_LOW_POWER;
|
||||
break;
|
||||
case DYTC_MODE_BALANCE:
|
||||
case DYTC_MODE_MMC_BALANCE:
|
||||
*profile = PLATFORM_PROFILE_BALANCED;
|
||||
break;
|
||||
case DYTC_MODE_PERFORM:
|
||||
@ -10165,7 +10230,6 @@ static int dytc_cql_command(int command, int *output)
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return cmd_err;
|
||||
}
|
||||
|
||||
@ -10222,7 +10286,10 @@ static void dytc_profile_refresh(void)
|
||||
int perfmode;
|
||||
|
||||
mutex_lock(&dytc_mutex);
|
||||
err = dytc_cql_command(DYTC_CMD_GET, &output);
|
||||
if (dytc_mmc_get_available)
|
||||
err = dytc_command(DYTC_CMD_MMC_GET, &output);
|
||||
else
|
||||
err = dytc_cql_command(DYTC_CMD_GET, &output);
|
||||
mutex_unlock(&dytc_mutex);
|
||||
if (err)
|
||||
return;
|
||||
@ -10271,6 +10338,16 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
|
||||
if (dytc_version >= 5) {
|
||||
dbg_printk(TPACPI_DBG_INIT,
|
||||
"DYTC version %d: thermal mode available\n", dytc_version);
|
||||
/*
|
||||
* Check if MMC_GET functionality available
|
||||
* Version > 6 and return success from MMC_GET command
|
||||
*/
|
||||
dytc_mmc_get_available = false;
|
||||
if (dytc_version >= 6) {
|
||||
err = dytc_command(DYTC_CMD_MMC_GET, &output);
|
||||
if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS))
|
||||
dytc_mmc_get_available = true;
|
||||
}
|
||||
/* Create platform_profile structure and register */
|
||||
err = platform_profile_register(&dytc_profile);
|
||||
/*
|
||||
@ -10473,6 +10550,111 @@ static struct ibm_struct kbdlang_driver_data = {
|
||||
.exit = kbdlang_exit,
|
||||
};
|
||||
|
||||
/*************************************************************************
|
||||
* DPRC(Dynamic Power Reduction Control) subdriver, for the Lenovo WWAN
|
||||
* and WLAN feature.
|
||||
*/
|
||||
#define DPRC_GET_WWAN_ANTENNA_TYPE 0x40000
|
||||
#define DPRC_WWAN_ANTENNA_TYPE_A_BIT BIT(4)
|
||||
#define DPRC_WWAN_ANTENNA_TYPE_B_BIT BIT(8)
|
||||
static bool has_antennatype;
|
||||
static int wwan_antennatype;
|
||||
|
||||
static int dprc_command(int command, int *output)
|
||||
{
|
||||
acpi_handle dprc_handle;
|
||||
|
||||
if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "DPRC", &dprc_handle))) {
|
||||
/* Platform doesn't support DPRC */
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!acpi_evalf(dprc_handle, output, NULL, "dd", command))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* METHOD_ERR gets returned on devices where few commands are not supported
|
||||
* for example command to get WWAN Antenna type command is not supported on
|
||||
* some devices.
|
||||
*/
|
||||
if (*output & METHOD_ERR)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_wwan_antenna(int *wwan_antennatype)
|
||||
{
|
||||
int output, err;
|
||||
|
||||
/* Get current Antenna type */
|
||||
err = dprc_command(DPRC_GET_WWAN_ANTENNA_TYPE, &output);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (output & DPRC_WWAN_ANTENNA_TYPE_A_BIT)
|
||||
*wwan_antennatype = 1;
|
||||
else if (output & DPRC_WWAN_ANTENNA_TYPE_B_BIT)
|
||||
*wwan_antennatype = 2;
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sysfs wwan antenna type entry */
|
||||
static ssize_t wwan_antenna_type_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
switch (wwan_antennatype) {
|
||||
case 1:
|
||||
return sysfs_emit(buf, "type a\n");
|
||||
case 2:
|
||||
return sysfs_emit(buf, "type b\n");
|
||||
default:
|
||||
return -ENODATA;
|
||||
}
|
||||
}
|
||||
static DEVICE_ATTR_RO(wwan_antenna_type);
|
||||
|
||||
static int tpacpi_dprc_init(struct ibm_init_struct *iibm)
|
||||
{
|
||||
int wwanantenna_err, err;
|
||||
|
||||
wwanantenna_err = get_wwan_antenna(&wwan_antennatype);
|
||||
/*
|
||||
* If support isn't available (ENODEV) then quit, but don't
|
||||
* return an error.
|
||||
*/
|
||||
if (wwanantenna_err == -ENODEV)
|
||||
return 0;
|
||||
|
||||
/* if there was an error return it */
|
||||
if (wwanantenna_err && (wwanantenna_err != -ENODEV))
|
||||
return wwanantenna_err;
|
||||
else if (!wwanantenna_err)
|
||||
has_antennatype = true;
|
||||
|
||||
if (has_antennatype) {
|
||||
err = sysfs_create_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dprc_exit(void)
|
||||
{
|
||||
if (has_antennatype)
|
||||
sysfs_remove_file(&tpacpi_pdev->dev.kobj, &dev_attr_wwan_antenna_type.attr);
|
||||
}
|
||||
|
||||
static struct ibm_struct dprc_driver_data = {
|
||||
.name = "dprc",
|
||||
.exit = dprc_exit,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************
|
||||
*
|
||||
@ -10977,6 +11159,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
|
||||
.init = tpacpi_kbdlang_init,
|
||||
.data = &kbdlang_driver_data,
|
||||
},
|
||||
{
|
||||
.init = tpacpi_dprc_init,
|
||||
.data = &dprc_driver_data,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
|
||||
|
@ -715,6 +715,32 @@ static const struct ts_dmi_data techbite_arc_11_6_data = {
|
||||
.properties = techbite_arc_11_6_props,
|
||||
};
|
||||
|
||||
static const struct property_entry teclast_tbook11_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 8),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 14),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1916),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1264),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-tbook11.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data teclast_tbook11_data = {
|
||||
.embedded_fw = {
|
||||
.name = "silead/gsl3692-teclast-tbook11.fw",
|
||||
.prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 },
|
||||
.length = 43560,
|
||||
.sha256 = { 0x9d, 0xb0, 0x3d, 0xf1, 0x00, 0x3c, 0xb5, 0x25,
|
||||
0x62, 0x8a, 0xa0, 0x93, 0x4b, 0xe0, 0x4e, 0x75,
|
||||
0xd1, 0x27, 0xb1, 0x65, 0x3c, 0xba, 0xa5, 0x0f,
|
||||
0xcd, 0xb4, 0xbe, 0x00, 0xbb, 0xf6, 0x43, 0x29 },
|
||||
},
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = teclast_tbook11_props,
|
||||
};
|
||||
|
||||
static const struct property_entry teclast_x3_plus_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
|
||||
@ -1243,6 +1269,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "G8316_272B"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Teclast Tbook 11 */
|
||||
.driver_data = (void *)&teclast_tbook11_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TbooK 11"),
|
||||
DMI_MATCH(DMI_PRODUCT_SKU, "E5A6_A1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Teclast X3 Plus */
|
||||
.driver_data = (void *)&teclast_x3_plus_data,
|
||||
@ -1355,7 +1390,7 @@ static void ts_dmi_add_props(struct i2c_client *client)
|
||||
|
||||
if (has_acpi_companion(dev) &&
|
||||
!strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) {
|
||||
error = device_add_properties(dev, ts_data->properties);
|
||||
error = device_create_managed_software_node(dev, ts_data->properties, NULL);
|
||||
if (error)
|
||||
dev_err(dev, "failed to add properties: %d\n", error);
|
||||
}
|
||||
|
@ -86,13 +86,12 @@ static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wmi_bmof_remove(struct wmi_device *wdev)
|
||||
static void wmi_bmof_remove(struct wmi_device *wdev)
|
||||
{
|
||||
struct bmof_priv *priv = dev_get_drvdata(&wdev->dev);
|
||||
|
||||
sysfs_remove_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
|
||||
kfree(priv->bmofdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id wmi_bmof_id_table[] = {
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include <uapi/linux/wmi.h>
|
||||
|
||||
ACPI_MODULE_NAME("wmi");
|
||||
MODULE_AUTHOR("Carlos Corbacho");
|
||||
MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@ -986,7 +985,6 @@ static int wmi_dev_remove(struct device *dev)
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
struct wmi_driver *wdriver =
|
||||
container_of(dev->driver, struct wmi_driver, driver);
|
||||
int ret = 0;
|
||||
|
||||
if (wdriver->filter_callback) {
|
||||
misc_deregister(&wblock->char_dev);
|
||||
@ -995,12 +993,12 @@ static int wmi_dev_remove(struct device *dev)
|
||||
}
|
||||
|
||||
if (wdriver->remove)
|
||||
ret = wdriver->remove(dev_to_wdev(dev));
|
||||
wdriver->remove(dev_to_wdev(dev));
|
||||
|
||||
if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
|
||||
dev_warn(dev, "failed to disable device\n");
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct class wmi_bus_class = {
|
||||
|
@ -26,8 +26,6 @@
|
||||
#define XO15_EBOOK_HID "XO15EBK"
|
||||
#define XO15_EBOOK_DEVICE_NAME "EBook Switch"
|
||||
|
||||
ACPI_MODULE_NAME(MODULE_NAME);
|
||||
|
||||
MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@ -66,8 +64,8 @@ static void ebook_switch_notify(struct acpi_device *device, u32 event)
|
||||
ebook_send_state(device);
|
||||
break;
|
||||
default:
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"Unsupported event [0x%x]\n", event));
|
||||
acpi_handle_debug(device->handle,
|
||||
"Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -344,16 +344,16 @@ struct ssam_request_spec_md {
|
||||
* request has been fully completed. The required transport buffer will be
|
||||
* allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_controller
|
||||
* *ctrl)``, returning the status of the request, which is zero on success and
|
||||
* negative on failure. The ``ctrl`` parameter is the controller via which the
|
||||
* request is being sent.
|
||||
* The generated function is defined as ``static int name(struct
|
||||
* ssam_controller *ctrl)``, returning the status of the request, which is
|
||||
* zero on success and negative on failure. The ``ctrl`` parameter is the
|
||||
* controller via which the request is being sent.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_N(name, spec...) \
|
||||
int name(struct ssam_controller *ctrl) \
|
||||
static int name(struct ssam_controller *ctrl) \
|
||||
{ \
|
||||
struct ssam_request_spec s = (struct ssam_request_spec)spec; \
|
||||
struct ssam_request rqst; \
|
||||
@ -383,17 +383,17 @@ struct ssam_request_spec_md {
|
||||
* returning once the request has been fully completed. The required transport
|
||||
* buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_controller
|
||||
* *ctrl, const atype *arg)``, returning the status of the request, which is
|
||||
* zero on success and negative on failure. The ``ctrl`` parameter is the
|
||||
* controller via which the request is sent. The request argument is specified
|
||||
* via the ``arg`` pointer.
|
||||
* The generated function is defined as ``static int name(struct
|
||||
* ssam_controller *ctrl, const atype *arg)``, returning the status of the
|
||||
* request, which is zero on success and negative on failure. The ``ctrl``
|
||||
* parameter is the controller via which the request is sent. The request
|
||||
* argument is specified via the ``arg`` pointer.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_W(name, atype, spec...) \
|
||||
int name(struct ssam_controller *ctrl, const atype *arg) \
|
||||
static int name(struct ssam_controller *ctrl, const atype *arg) \
|
||||
{ \
|
||||
struct ssam_request_spec s = (struct ssam_request_spec)spec; \
|
||||
struct ssam_request rqst; \
|
||||
@ -424,17 +424,17 @@ struct ssam_request_spec_md {
|
||||
* request itself, returning once the request has been fully completed. The
|
||||
* required transport buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_controller
|
||||
* *ctrl, rtype *ret)``, returning the status of the request, which is zero on
|
||||
* success and negative on failure. The ``ctrl`` parameter is the controller
|
||||
* via which the request is sent. The request's return value is written to the
|
||||
* memory pointed to by the ``ret`` parameter.
|
||||
* The generated function is defined as ``static int name(struct
|
||||
* ssam_controller *ctrl, rtype *ret)``, returning the status of the request,
|
||||
* which is zero on success and negative on failure. The ``ctrl`` parameter is
|
||||
* the controller via which the request is sent. The request's return value is
|
||||
* written to the memory pointed to by the ``ret`` parameter.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_R(name, rtype, spec...) \
|
||||
int name(struct ssam_controller *ctrl, rtype *ret) \
|
||||
static int name(struct ssam_controller *ctrl, rtype *ret) \
|
||||
{ \
|
||||
struct ssam_request_spec s = (struct ssam_request_spec)spec; \
|
||||
struct ssam_request rqst; \
|
||||
@ -483,17 +483,17 @@ struct ssam_request_spec_md {
|
||||
* returning once the request has been fully completed. The required transport
|
||||
* buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_controller
|
||||
* *ctrl, u8 tid, u8 iid)``, returning the status of the request, which is
|
||||
* zero on success and negative on failure. The ``ctrl`` parameter is the
|
||||
* controller via which the request is sent, ``tid`` the target ID for the
|
||||
* request, and ``iid`` the instance ID.
|
||||
* The generated function is defined as ``static int name(struct
|
||||
* ssam_controller *ctrl, u8 tid, u8 iid)``, returning the status of the
|
||||
* request, which is zero on success and negative on failure. The ``ctrl``
|
||||
* parameter is the controller via which the request is sent, ``tid`` the
|
||||
* target ID for the request, and ``iid`` the instance ID.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_MD_N(name, spec...) \
|
||||
int name(struct ssam_controller *ctrl, u8 tid, u8 iid) \
|
||||
static int name(struct ssam_controller *ctrl, u8 tid, u8 iid) \
|
||||
{ \
|
||||
struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
|
||||
struct ssam_request rqst; \
|
||||
@ -524,18 +524,18 @@ struct ssam_request_spec_md {
|
||||
* the request itself, returning once the request has been fully completed.
|
||||
* The required transport buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_controller
|
||||
* *ctrl, u8 tid, u8 iid, const atype *arg)``, returning the status of the
|
||||
* request, which is zero on success and negative on failure. The ``ctrl``
|
||||
* parameter is the controller via which the request is sent, ``tid`` the
|
||||
* target ID for the request, and ``iid`` the instance ID. The request argument
|
||||
* is specified via the ``arg`` pointer.
|
||||
* The generated function is defined as ``static int name(struct
|
||||
* ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg)``, returning the
|
||||
* status of the request, which is zero on success and negative on failure.
|
||||
* The ``ctrl`` parameter is the controller via which the request is sent,
|
||||
* ``tid`` the target ID for the request, and ``iid`` the instance ID. The
|
||||
* request argument is specified via the ``arg`` pointer.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_MD_W(name, atype, spec...) \
|
||||
int name(struct ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg)\
|
||||
static int name(struct ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg) \
|
||||
{ \
|
||||
struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
|
||||
struct ssam_request rqst; \
|
||||
@ -567,18 +567,18 @@ struct ssam_request_spec_md {
|
||||
* execution of the request itself, returning once the request has been fully
|
||||
* completed. The required transport buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_controller
|
||||
* *ctrl, u8 tid, u8 iid, rtype *ret)``, returning the status of the request,
|
||||
* which is zero on success and negative on failure. The ``ctrl`` parameter is
|
||||
* the controller via which the request is sent, ``tid`` the target ID for the
|
||||
* request, and ``iid`` the instance ID. The request's return value is written
|
||||
* to the memory pointed to by the ``ret`` parameter.
|
||||
* The generated function is defined as ``static int name(struct
|
||||
* ssam_controller *ctrl, u8 tid, u8 iid, rtype *ret)``, returning the status
|
||||
* of the request, which is zero on success and negative on failure. The
|
||||
* ``ctrl`` parameter is the controller via which the request is sent, ``tid``
|
||||
* the target ID for the request, and ``iid`` the instance ID. The request's
|
||||
* return value is written to the memory pointed to by the ``ret`` parameter.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_MD_R(name, rtype, spec...) \
|
||||
int name(struct ssam_controller *ctrl, u8 tid, u8 iid, rtype *ret) \
|
||||
static int name(struct ssam_controller *ctrl, u8 tid, u8 iid, rtype *ret) \
|
||||
{ \
|
||||
struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \
|
||||
struct ssam_request rqst; \
|
||||
|
@ -336,17 +336,18 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
|
||||
* request has been fully completed. The required transport buffer will be
|
||||
* allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_device *sdev)``,
|
||||
* returning the status of the request, which is zero on success and negative
|
||||
* on failure. The ``sdev`` parameter specifies both the target device of the
|
||||
* request and by association the controller via which the request is sent.
|
||||
* The generated function is defined as ``static int name(struct ssam_device
|
||||
* *sdev)``, returning the status of the request, which is zero on success and
|
||||
* negative on failure. The ``sdev`` parameter specifies both the target
|
||||
* device of the request and by association the controller via which the
|
||||
* request is sent.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_CL_N(name, spec...) \
|
||||
SSAM_DEFINE_SYNC_REQUEST_MD_N(__raw_##name, spec) \
|
||||
int name(struct ssam_device *sdev) \
|
||||
static int name(struct ssam_device *sdev) \
|
||||
{ \
|
||||
return __raw_##name(sdev->ctrl, sdev->uid.target, \
|
||||
sdev->uid.instance); \
|
||||
@ -368,19 +369,19 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
|
||||
* itself, returning once the request has been fully completed. The required
|
||||
* transport buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_device *sdev,
|
||||
* const atype *arg)``, returning the status of the request, which is zero on
|
||||
* success and negative on failure. The ``sdev`` parameter specifies both the
|
||||
* target device of the request and by association the controller via which
|
||||
* the request is sent. The request's argument is specified via the ``arg``
|
||||
* pointer.
|
||||
* The generated function is defined as ``static int name(struct ssam_device
|
||||
* *sdev, const atype *arg)``, returning the status of the request, which is
|
||||
* zero on success and negative on failure. The ``sdev`` parameter specifies
|
||||
* both the target device of the request and by association the controller via
|
||||
* which the request is sent. The request's argument is specified via the
|
||||
* ``arg`` pointer.
|
||||
*
|
||||
* Refer to ssam_request_sync_onstack() for more details on the behavior of
|
||||
* the generated function.
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_CL_W(name, atype, spec...) \
|
||||
SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_##name, atype, spec) \
|
||||
int name(struct ssam_device *sdev, const atype *arg) \
|
||||
static int name(struct ssam_device *sdev, const atype *arg) \
|
||||
{ \
|
||||
return __raw_##name(sdev->ctrl, sdev->uid.target, \
|
||||
sdev->uid.instance, arg); \
|
||||
@ -402,8 +403,8 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
|
||||
* itself, returning once the request has been fully completed. The required
|
||||
* transport buffer will be allocated on the stack.
|
||||
*
|
||||
* The generated function is defined as ``int name(struct ssam_device *sdev,
|
||||
* rtype *ret)``, returning the status of the request, which is zero on
|
||||
* The generated function is defined as ``static int name(struct ssam_device
|
||||
* *sdev, rtype *ret)``, returning the status of the request, which is zero on
|
||||
* success and negative on failure. The ``sdev`` parameter specifies both the
|
||||
* target device of the request and by association the controller via which
|
||||
* the request is sent. The request's return value is written to the memory
|
||||
@ -414,7 +415,7 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
|
||||
*/
|
||||
#define SSAM_DEFINE_SYNC_REQUEST_CL_R(name, rtype, spec...) \
|
||||
SSAM_DEFINE_SYNC_REQUEST_MD_R(__raw_##name, rtype, spec) \
|
||||
int name(struct ssam_device *sdev, rtype *ret) \
|
||||
static int name(struct ssam_device *sdev, rtype *ret) \
|
||||
{ \
|
||||
return __raw_##name(sdev->ctrl, sdev->uid.target, \
|
||||
sdev->uid.instance, ret); \
|
||||
|
@ -37,7 +37,7 @@ struct wmi_driver {
|
||||
const struct wmi_device_id *id_table;
|
||||
|
||||
int (*probe)(struct wmi_device *wdev, const void *context);
|
||||
int (*remove)(struct wmi_device *wdev);
|
||||
void (*remove)(struct wmi_device *wdev);
|
||||
void (*notify)(struct wmi_device *device, union acpi_object *data);
|
||||
long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd,
|
||||
struct wmi_ioctl_buffer *arg);
|
||||
|
146
include/uapi/linux/surface_aggregator/dtx.h
Normal file
146
include/uapi/linux/surface_aggregator/dtx.h
Normal file
@ -0,0 +1,146 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* Surface DTX (clipboard detachment system driver) user-space interface.
|
||||
*
|
||||
* Definitions, structs, and IOCTLs for the /dev/surface/dtx misc device. This
|
||||
* device allows user-space to control the clipboard detachment process on
|
||||
* Surface Book series devices.
|
||||
*
|
||||
* Copyright (C) 2020-2021 Maximilian Luz <luzmaximilian@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_DTX_H
|
||||
#define _UAPI_LINUX_SURFACE_AGGREGATOR_DTX_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Status/error categories */
|
||||
#define SDTX_CATEGORY_STATUS 0x0000
|
||||
#define SDTX_CATEGORY_RUNTIME_ERROR 0x1000
|
||||
#define SDTX_CATEGORY_HARDWARE_ERROR 0x2000
|
||||
#define SDTX_CATEGORY_UNKNOWN 0xf000
|
||||
|
||||
#define SDTX_CATEGORY_MASK 0xf000
|
||||
#define SDTX_CATEGORY(value) ((value) & SDTX_CATEGORY_MASK)
|
||||
|
||||
#define SDTX_STATUS(code) ((code) | SDTX_CATEGORY_STATUS)
|
||||
#define SDTX_ERR_RT(code) ((code) | SDTX_CATEGORY_RUNTIME_ERROR)
|
||||
#define SDTX_ERR_HW(code) ((code) | SDTX_CATEGORY_HARDWARE_ERROR)
|
||||
#define SDTX_UNKNOWN(code) ((code) | SDTX_CATEGORY_UNKNOWN)
|
||||
|
||||
#define SDTX_SUCCESS(value) (SDTX_CATEGORY(value) == SDTX_CATEGORY_STATUS)
|
||||
|
||||
/* Latch status values */
|
||||
#define SDTX_LATCH_CLOSED SDTX_STATUS(0x00)
|
||||
#define SDTX_LATCH_OPENED SDTX_STATUS(0x01)
|
||||
|
||||
/* Base state values */
|
||||
#define SDTX_BASE_DETACHED SDTX_STATUS(0x00)
|
||||
#define SDTX_BASE_ATTACHED SDTX_STATUS(0x01)
|
||||
|
||||
/* Runtime errors (non-critical) */
|
||||
#define SDTX_DETACH_NOT_FEASIBLE SDTX_ERR_RT(0x01)
|
||||
#define SDTX_DETACH_TIMEDOUT SDTX_ERR_RT(0x02)
|
||||
|
||||
/* Hardware errors (critical) */
|
||||
#define SDTX_ERR_FAILED_TO_OPEN SDTX_ERR_HW(0x01)
|
||||
#define SDTX_ERR_FAILED_TO_REMAIN_OPEN SDTX_ERR_HW(0x02)
|
||||
#define SDTX_ERR_FAILED_TO_CLOSE SDTX_ERR_HW(0x03)
|
||||
|
||||
/* Base types */
|
||||
#define SDTX_DEVICE_TYPE_HID 0x0100
|
||||
#define SDTX_DEVICE_TYPE_SSH 0x0200
|
||||
|
||||
#define SDTX_DEVICE_TYPE_MASK 0x0f00
|
||||
#define SDTX_DEVICE_TYPE(value) ((value) & SDTX_DEVICE_TYPE_MASK)
|
||||
|
||||
#define SDTX_BASE_TYPE_HID(id) ((id) | SDTX_DEVICE_TYPE_HID)
|
||||
#define SDTX_BASE_TYPE_SSH(id) ((id) | SDTX_DEVICE_TYPE_SSH)
|
||||
|
||||
/**
|
||||
* enum sdtx_device_mode - Mode describing how (and if) the clipboard is
|
||||
* attached to the base of the device.
|
||||
* @SDTX_DEVICE_MODE_TABLET: The clipboard is detached from the base and the
|
||||
* device operates as tablet.
|
||||
* @SDTX_DEVICE_MODE_LAPTOP: The clipboard is attached normally to the base
|
||||
* and the device operates as laptop.
|
||||
* @SDTX_DEVICE_MODE_STUDIO: The clipboard is attached to the base in reverse.
|
||||
* The device operates as tablet with keyboard and
|
||||
* touchpad deactivated, however, the base battery
|
||||
* and, if present in the specific device model, dGPU
|
||||
* are available to the system.
|
||||
*/
|
||||
enum sdtx_device_mode {
|
||||
SDTX_DEVICE_MODE_TABLET = 0x00,
|
||||
SDTX_DEVICE_MODE_LAPTOP = 0x01,
|
||||
SDTX_DEVICE_MODE_STUDIO = 0x02,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdtx_event - Event provided by reading from the DTX device file.
|
||||
* @length: Length of the event payload, in bytes.
|
||||
* @code: Event code, detailing what type of event this is.
|
||||
* @data: Payload of the event, containing @length bytes.
|
||||
*
|
||||
* See &enum sdtx_event_code for currently valid event codes.
|
||||
*/
|
||||
struct sdtx_event {
|
||||
__u16 length;
|
||||
__u16 code;
|
||||
__u8 data[];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* enum sdtx_event_code - Code describing the type of an event.
|
||||
* @SDTX_EVENT_REQUEST: Detachment request event type.
|
||||
* @SDTX_EVENT_CANCEL: Cancel detachment process event type.
|
||||
* @SDTX_EVENT_BASE_CONNECTION: Base/clipboard connection change event type.
|
||||
* @SDTX_EVENT_LATCH_STATUS: Latch status change event type.
|
||||
* @SDTX_EVENT_DEVICE_MODE: Device mode change event type.
|
||||
*
|
||||
* Used in &struct sdtx_event to describe the type of the event. Further event
|
||||
* codes are reserved for future use. Any event parser should be able to
|
||||
* gracefully handle unknown events, i.e. by simply skipping them.
|
||||
*
|
||||
* Consult the DTX user-space interface documentation for details regarding
|
||||
* the individual event types.
|
||||
*/
|
||||
enum sdtx_event_code {
|
||||
SDTX_EVENT_REQUEST = 1,
|
||||
SDTX_EVENT_CANCEL = 2,
|
||||
SDTX_EVENT_BASE_CONNECTION = 3,
|
||||
SDTX_EVENT_LATCH_STATUS = 4,
|
||||
SDTX_EVENT_DEVICE_MODE = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdtx_base_info - Describes if and what type of base is connected.
|
||||
* @state: The state of the connection. Valid values are %SDTX_BASE_DETACHED,
|
||||
* %SDTX_BASE_ATTACHED, and %SDTX_DETACH_NOT_FEASIBLE (in case a base
|
||||
* is attached but low clipboard battery prevents detachment). Other
|
||||
* values are currently reserved.
|
||||
* @base_id: The type of base connected. Zero if no base is connected.
|
||||
*/
|
||||
struct sdtx_base_info {
|
||||
__u16 state;
|
||||
__u16 base_id;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* IOCTLs */
|
||||
#define SDTX_IOCTL_EVENTS_ENABLE _IO(0xa5, 0x21)
|
||||
#define SDTX_IOCTL_EVENTS_DISABLE _IO(0xa5, 0x22)
|
||||
|
||||
#define SDTX_IOCTL_LATCH_LOCK _IO(0xa5, 0x23)
|
||||
#define SDTX_IOCTL_LATCH_UNLOCK _IO(0xa5, 0x24)
|
||||
|
||||
#define SDTX_IOCTL_LATCH_REQUEST _IO(0xa5, 0x25)
|
||||
#define SDTX_IOCTL_LATCH_CONFIRM _IO(0xa5, 0x26)
|
||||
#define SDTX_IOCTL_LATCH_HEARTBEAT _IO(0xa5, 0x27)
|
||||
#define SDTX_IOCTL_LATCH_CANCEL _IO(0xa5, 0x28)
|
||||
|
||||
#define SDTX_IOCTL_GET_BASE_INFO _IOR(0xa5, 0x29, struct sdtx_base_info)
|
||||
#define SDTX_IOCTL_GET_DEVICE_MODE _IOR(0xa5, 0x2a, __u16)
|
||||
#define SDTX_IOCTL_GET_LATCH_STATUS _IOR(0xa5, 0x2b, __u16)
|
||||
|
||||
#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_DTX_H */
|
@ -15,7 +15,7 @@ struct process_cmd_struct {
|
||||
int arg;
|
||||
};
|
||||
|
||||
static const char *version_str = "v1.8";
|
||||
static const char *version_str = "v1.9";
|
||||
static const int supported_api_ver = 1;
|
||||
static struct isst_if_platform_info isst_platform_info;
|
||||
static char *progname;
|
||||
@ -381,6 +381,18 @@ static void set_cpu_online_offline(int cpu, int state)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void force_all_cpus_online(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(stderr, "Forcing all CPUs online\n");
|
||||
|
||||
for (i = 0; i < topo_max_cpus; ++i)
|
||||
set_cpu_online_offline(i, 1);
|
||||
|
||||
unlink("/var/run/isst_cpu_topology.dat");
|
||||
}
|
||||
|
||||
#define MAX_PACKAGE_COUNT 8
|
||||
#define MAX_DIE_PER_PACKAGE 2
|
||||
static void for_each_online_package_in_set(void (*callback)(int, void *, void *,
|
||||
@ -959,6 +971,10 @@ static void isst_print_extended_platform_info(void)
|
||||
fprintf(outf, "Intel(R) SST-BF (feature base-freq) is not supported\n");
|
||||
|
||||
ret = isst_read_pm_config(i, &cp_state, &cp_cap);
|
||||
if (ret) {
|
||||
fprintf(outf, "Intel(R) SST-CP (feature core-power) status is unknown\n");
|
||||
return;
|
||||
}
|
||||
if (cp_cap)
|
||||
fprintf(outf, "Intel(R) SST-CP (feature core-power) is supported\n");
|
||||
else
|
||||
@ -2763,6 +2779,7 @@ static void usage(void)
|
||||
printf("\t[-f|--format] : output format [json|text]. Default: text\n");
|
||||
printf("\t[-h|--help] : Print help\n");
|
||||
printf("\t[-i|--info] : Print platform information\n");
|
||||
printf("\t[-a|--all-cpus-online] : Force online every CPU in the system\n");
|
||||
printf("\t[-o|--out] : Output file\n");
|
||||
printf("\t\t\tDefault : stderr\n");
|
||||
printf("\t[-p|--pause] : Delay between two mail box commands in milliseconds\n");
|
||||
@ -2791,7 +2808,6 @@ static void usage(void)
|
||||
static void print_version(void)
|
||||
{
|
||||
fprintf(outf, "Version %s\n", version_str);
|
||||
fprintf(outf, "Build date %s time %s\n", __DATE__, __TIME__);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -2800,11 +2816,12 @@ static void cmdline(int argc, char **argv)
|
||||
const char *pathname = "/dev/isst_interface";
|
||||
char *ptr;
|
||||
FILE *fp;
|
||||
int opt;
|
||||
int opt, force_cpus_online = 0;
|
||||
int option_index = 0;
|
||||
int ret;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{ "all-cpus-online", no_argument, 0, 'a' },
|
||||
{ "cpu", required_argument, 0, 'c' },
|
||||
{ "debug", no_argument, 0, 'd' },
|
||||
{ "format", required_argument, 0, 'f' },
|
||||
@ -2840,9 +2857,12 @@ static void cmdline(int argc, char **argv)
|
||||
}
|
||||
|
||||
progname = argv[0];
|
||||
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:v", long_options,
|
||||
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:va", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
force_cpus_online = 1;
|
||||
break;
|
||||
case 'c':
|
||||
parse_cpu_command(optarg);
|
||||
break;
|
||||
@ -2892,6 +2912,8 @@ static void cmdline(int argc, char **argv)
|
||||
exit(0);
|
||||
}
|
||||
set_max_cpu_num();
|
||||
if (force_cpus_online)
|
||||
force_all_cpus_online();
|
||||
store_cpu_topology();
|
||||
set_cpu_present_cpu_mask();
|
||||
set_cpu_target_cpu_mask();
|
||||
|
@ -25,10 +25,14 @@ static void printcpulist(int str_len, char *str, int mask_size,
|
||||
index = snprintf(&str[curr_index],
|
||||
str_len - curr_index, ",");
|
||||
curr_index += index;
|
||||
if (curr_index >= str_len)
|
||||
break;
|
||||
}
|
||||
index = snprintf(&str[curr_index], str_len - curr_index, "%d",
|
||||
i);
|
||||
curr_index += index;
|
||||
if (curr_index >= str_len)
|
||||
break;
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
@ -64,10 +68,14 @@ static void printcpumask(int str_len, char *str, int mask_size,
|
||||
index = snprintf(&str[curr_index], str_len - curr_index, "%08x",
|
||||
mask[i]);
|
||||
curr_index += index;
|
||||
if (curr_index >= str_len)
|
||||
break;
|
||||
if (i) {
|
||||
strncat(&str[curr_index], ",", str_len - curr_index);
|
||||
curr_index++;
|
||||
}
|
||||
if (curr_index >= str_len)
|
||||
break;
|
||||
}
|
||||
|
||||
free(mask);
|
||||
@ -185,7 +193,7 @@ static void _isst_pbf_display_information(int cpu, FILE *outf, int level,
|
||||
int disp_level)
|
||||
{
|
||||
char header[256];
|
||||
char value[256];
|
||||
char value[512];
|
||||
|
||||
snprintf(header, sizeof(header), "speed-select-base-freq-properties");
|
||||
format_and_print(outf, disp_level, header, NULL);
|
||||
@ -349,7 +357,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
|
||||
struct isst_pkg_ctdp *pkg_dev)
|
||||
{
|
||||
char header[256];
|
||||
char value[256];
|
||||
char value[512];
|
||||
static int level;
|
||||
int i;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user