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:
Linus Torvalds 2021-04-26 10:58:33 -07:00
commit 90035c28f1
57 changed files with 4640 additions and 294 deletions

View 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.

View File

@ -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
-----------------

View File

@ -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:

View 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.

View File

@ -11,6 +11,7 @@ This is the documentation for client drivers themselves. Refer to
:maxdepth: 1
cdev
dtx
san
.. only:: subproject and html

View File

@ -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

View File

@ -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/

View File

@ -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) },
{ }

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;
}

View 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");

File diff suppressed because it is too large Load Diff

View 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");

View File

@ -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");

View File

@ -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>

View File

@ -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

View 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");

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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[] = {

View File

@ -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");

View File

@ -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[] = {

View File

@ -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[] = {

View File

@ -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");

View File

@ -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 },

View 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");

View File

@ -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;
}

View File

@ -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;

View File

@ -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[] = {

View File

@ -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[] = {

View File

@ -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;

View File

@ -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, &reg);
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,

View File

@ -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 */

View File

@ -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:
/*

View File

@ -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);

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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"),
},
},
{

View File

@ -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)

View File

@ -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);
}

View File

@ -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[] = {

View File

@ -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 = {

View File

@ -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;
}
}

View File

@ -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; \

View File

@ -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); \

View File

@ -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);

View 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 */

View File

@ -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();

View File

@ -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;