Commit ea5f6ad9 authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull x86 platform driver updates from Hans de Goede:

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

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

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

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

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

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

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

 - WMI core:
    - New WMI driver development guide

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

 - Miscellaneous cleanups / fixes / improvements

* tag 'platform-drivers-x86-v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (128 commits)
  platform/x86: Add new MeeGoPad ANX7428 Type-C Cross Switch driver
  devm-helpers: Fix a misspelled cancellation in the comments
  tools arch x86: Add dell-uart-backlight-emulator
  platform/x86: Add new Dell UART backlight driver
  platform/x86: x86-android-tablets: Create LED device for Xiaomi Pad 2 bottom bezel touch buttons
  platform/x86: x86-android-tablets: Xiaomi pad2 RGB LED fwnode updates
  platform/x86: x86-android-tablets: Pass struct device to init()
  platform/x86/amd: pmc: Add new ACPI ID AMDI000B
  platform/x86/amd: pmf: Add new ACPI ID AMDI0105
  platform/x86: p2sb: Don't init until unassigned resources have been assigned
  platform/surface: aggregator: Log critical errors during SAM probing
  platform/x86: ISST: Support SST-BF and SST-TF per level
  platform/x86/fujitsu-laptop: Replace sprintf() with sysfs_emit()
  tools/power/x86/intel-speed-select: v1.19 release
  tools/power/x86/intel-speed-select: Display CPU as None for -1
  tools/power/x86/intel-speed-select: SST BF/TF support per level
  tools/power/x86/intel-speed-select: Increase number of CPUs displayed
  tools/power/x86/intel-speed-select: Present all TRL levels for turbo-freq
  tools/power/x86/intel-speed-select: Fix display for unsupported levels
  tools/power/x86/intel-speed-select: Support multiple dies
  ...
parents b426433c 2513563e
What: /sys/kernel/debug/msi-wmi-platform-<wmi_device_name>/*
Date: April 2024
KernelVersion: 6.10
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
This file allows to execute the associated WMI method with the same name.
To start the execution, write a buffer containing the method arguments
at file offset 0. Partial writes or writes at a different offset are not
supported.
The buffer returned by the WMI method can then be read from the file.
See Documentation/wmi/devices/msi-wmi-platform.rst for details.
......@@ -126,6 +126,14 @@ Description:
Change the mini-LED mode:
* 0 - Single-zone,
* 1 - Multi-zone
* 2 - Multi-zone strong (available on newer generation mini-led)
What: /sys/devices/platform/<platform>/available_mini_led_mode
Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
List the available mini-led modes.
What: /sys/devices/platform/<platform>/ppt_pl1_spl
Date: Jun 2023
......@@ -186,3 +194,21 @@ Contact: "Luke Jones" <luke@ljones.dev>
Description:
Set the target temperature limit of the Nvidia dGPU:
* min=75, max=87
What: /sys/devices/platform/<platform>/boot_sound
Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
Set if the BIOS POST sound is played on boot.
* 0 - False,
* 1 - True
What: /sys/devices/platform/<platform>/mcu_powersave
Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
Set if the MCU can go in to low-power mode on system sleep
* 0 - False,
* 1 - True
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/platform/acer,aspire1-ec.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Acer Aspire 1 Embedded Controller
maintainers:
- Nikita Travkin <nikita@trvn.ru>
description:
The Acer Aspire 1 laptop uses an embedded controller to control battery
and charging as well as to provide a set of misc features such as the
laptop lid status and HPD events for the USB Type-C DP alt mode.
properties:
compatible:
const: acer,aspire1-ec
reg:
const: 0x76
interrupts:
maxItems: 1
connector:
$ref: /schemas/connector/usb-connector.yaml#
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
embedded-controller@76 {
compatible = "acer,aspire1-ec";
reg = <0x76>;
interrupts-extended = <&tlmm 30 IRQ_TYPE_LEVEL_LOW>;
connector {
compatible = "usb-c-connector";
port {
ec_dp_in: endpoint {
remote-endpoint = <&mdss_dp_out>;
};
};
};
};
};
.. SPDX-License-Identifier: GPL-2.0-or-later
===================================================
MSI WMI Platform Features driver (msi-wmi-platform)
===================================================
Introduction
============
Many MSI notebooks support various features like reading fan sensors. This features are controlled
by the embedded controller, with the ACPI firmware exposing a standard ACPI WMI interface on top
of the embedded controller interface.
WMI interface description
=========================
The WMI interface description can be decoded from the embedded binary MOF (bmof)
data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
::
[WMI, Locale("MS\0x409"),
Description("This class contains the definition of the package used in other classes"),
guid("{ABBC0F60-8EA1-11d1-00A0-C90629100000}")]
class Package {
[WmiDataId(1), read, write, Description("16 bytes of data")] uint8 Bytes[16];
};
[WMI, Locale("MS\0x409"),
Description("This class contains the definition of the package used in other classes"),
guid("{ABBC0F63-8EA1-11d1-00A0-C90629100000}")]
class Package_32 {
[WmiDataId(1), read, write, Description("32 bytes of data")] uint8 Bytes[32];
};
[WMI, Dynamic, Provider("WmiProv"), Locale("MS\0x409"),
Description("Class used to operate methods on a package"),
guid("{ABBC0F6E-8EA1-11d1-00A0-C90629100000}")]
class MSI_ACPI {
[key, read] string InstanceName;
[read] boolean Active;
[WmiMethodId(1), Implemented, read, write, Description("Return the contents of a package")]
void GetPackage([out, id(0)] Package Data);
[WmiMethodId(2), Implemented, read, write, Description("Set the contents of a package")]
void SetPackage([in, id(0)] Package Data);
[WmiMethodId(3), Implemented, read, write, Description("Return the contents of a package")]
void Get_EC([out, id(0)] Package_32 Data);
[WmiMethodId(4), Implemented, read, write, Description("Set the contents of a package")]
void Set_EC([in, id(0)] Package_32 Data);
[WmiMethodId(5), Implemented, read, write, Description("Return the contents of a package")]
void Get_BIOS([in, out, id(0)] Package_32 Data);
[WmiMethodId(6), Implemented, read, write, Description("Set the contents of a package")]
void Set_BIOS([in, out, id(0)] Package_32 Data);
[WmiMethodId(7), Implemented, read, write, Description("Return the contents of a package")]
void Get_SMBUS([in, out, id(0)] Package_32 Data);
[WmiMethodId(8), Implemented, read, write, Description("Set the contents of a package")]
void Set_SMBUS([in, out, id(0)] Package_32 Data);
[WmiMethodId(9), Implemented, read, write, Description("Return the contents of a package")]
void Get_MasterBattery([in, out, id(0)] Package_32 Data);
[WmiMethodId(10), Implemented, read, write, Description("Set the contents of a package")]
void Set_MasterBattery([in, out, id(0)] Package_32 Data);
[WmiMethodId(11), Implemented, read, write, Description("Return the contents of a package")]
void Get_SlaveBattery([in, out, id(0)] Package_32 Data);
[WmiMethodId(12), Implemented, read, write, Description("Set the contents of a package")]
void Set_SlaveBattery([in, out, id(0)] Package_32 Data);
[WmiMethodId(13), Implemented, read, write, Description("Return the contents of a package")]
void Get_Temperature([in, out, id(0)] Package_32 Data);
[WmiMethodId(14), Implemented, read, write, Description("Set the contents of a package")]
void Set_Temperature([in, out, id(0)] Package_32 Data);
[WmiMethodId(15), Implemented, read, write, Description("Return the contents of a package")]
void Get_Thermal([in, out, id(0)] Package_32 Data);
[WmiMethodId(16), Implemented, read, write, Description("Set the contents of a package")]
void Set_Thermal([in, out, id(0)] Package_32 Data);
[WmiMethodId(17), Implemented, read, write, Description("Return the contents of a package")]
void Get_Fan([in, out, id(0)] Package_32 Data);
[WmiMethodId(18), Implemented, read, write, Description("Set the contents of a package")]
void Set_Fan([in, out, id(0)] Package_32 Data);
[WmiMethodId(19), Implemented, read, write, Description("Return the contents of a package")]
void Get_Device([in, out, id(0)] Package_32 Data);
[WmiMethodId(20), Implemented, read, write, Description("Set the contents of a package")]
void Set_Device([in, out, id(0)] Package_32 Data);
[WmiMethodId(21), Implemented, read, write, Description("Return the contents of a package")]
void Get_Power([in, out, id(0)] Package_32 Data);
[WmiMethodId(22), Implemented, read, write, Description("Set the contents of a package")]
void Set_Power([in, out, id(0)] Package_32 Data);
[WmiMethodId(23), Implemented, read, write, Description("Return the contents of a package")]
void Get_Debug([in, out, id(0)] Package_32 Data);
[WmiMethodId(24), Implemented, read, write, Description("Set the contents of a package")]
void Set_Debug([in, out, id(0)] Package_32 Data);
[WmiMethodId(25), Implemented, read, write, Description("Return the contents of a package")]
void Get_AP([in, out, id(0)] Package_32 Data);
[WmiMethodId(26), Implemented, read, write, Description("Set the contents of a package")]
void Set_AP([in, out, id(0)] Package_32 Data);
[WmiMethodId(27), Implemented, read, write, Description("Return the contents of a package")]
void Get_Data([in, out, id(0)] Package_32 Data);
[WmiMethodId(28), Implemented, read, write, Description("Set the contents of a package")]
void Set_Data([in, out, id(0)] Package_32 Data);
[WmiMethodId(29), Implemented, read, write, Description("Return the contents of a package")]
void Get_WMI([out, id(0)] Package_32 Data);
};
Due to a peculiarity in how Windows handles the ``CreateByteField()`` ACPI operator (errors only
happen when a invalid byte field is ultimately accessed), all methods require a 32 byte input
buffer, even if the Binay MOF says otherwise.
The input buffer contains a single byte to select the subfeature to be accessed and 31 bytes of
input data, the meaning of which depends on the subfeature being accessed.
The output buffer contains a singe byte which signals success or failure (``0x00`` on failure)
and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.
WMI method Get_EC()
-------------------
Returns embedded controller information, the selected subfeature does not matter. The output
data contains a flag byte and a 28 byte controller firmware version string.
The first 4 bits of the flag byte contain the minor version of the embedded controller interface,
with the next 2 bits containing the major version of the embedded controller interface.
The 7th bit signals if the embedded controller page chaged (exact meaning is unknown), and the
last bit signals if the platform is a Tigerlake platform.
The MSI software seems to only use this interface when the last bit is set.
WMI method Get_Fan()
--------------------
Fan speed sensors can be accessed by selecting subfeature ``0x00``. The output data contains
up to four 16-bit fan speed readings in big-endian format. Most machines do not support all
four fan speed sensors, so the remaining reading are hardcoded to ``0x0000``.
The fan RPM readings can be calculated with the following formula:
RPM = 480000 / <fan speed reading>
If the fan speed reading is zero, then the fan RPM is zero too.
WMI method Get_WMI()
--------------------
Returns the version of the ACPI WMI interface, the selected subfeature does not matter.
The output data contains two bytes, the first one contains the major version and the last one
contains the minor revision of the ACPI WMI interface.
The MSI software seems to only use this interface when the major version is greater than two.
Reverse-Engineering the MSI WMI Platform interface
==================================================
.. warning:: Randomly poking the embedded controller interface can potentially cause damage
to the machine and other unwanted side effects, please be careful.
The underlying embedded controller interface is used by the ``msi-ec`` driver, and it seems
that many methods just copy a part of the embedded controller memory into the output buffer.
This means that the remaining WMI methods can be reverse-engineered by looking which part of
the embedded controller memory is accessed by the ACPI AML code. The driver also supports a
debugfs interface for directly executing WMI methods. Additionally, any safety checks regarding
unsupported hardware can be disabled by loading the module with ``force=true``.
More information about the MSI embedded controller interface can be found at the
`msi-ec project <https://github.com/BeardOverflow/msi-ec>`_.
Special thanks go to github user `glpnk` for showing how to decode the fan speed readings.
.. SPDX-License-Identifier: GPL-2.0-or-later
============================
WMI driver development guide
============================
The WMI subsystem provides a rich driver API for implementing WMI drivers,
documented at Documentation/driver-api/wmi.rst. This document will serve
as an introductory guide for WMI driver writers using this API. It is supposed
to be a successor to the original LWN article [1]_ which deals with WMI drivers
using the deprecated GUID-based WMI interface.
Obtaining WMI device information
--------------------------------
Before developing an WMI driver, information about the WMI device in question
must be obtained. The `lswmi <https://pypi.org/project/lswmi>`_ utility can be
used to extract detailed WMI device information using the following command:
::
lswmi -V
The resulting output will contain information about all WMI devices available on
a given machine, plus some extra information.
In order to find out more about the interface used to communicate with a WMI device,
the `bmfdec <https://github.com/pali/bmfdec>`_ utilities can be used to decode
the Binary MOF (Managed Object Format) information used to describe WMI devices.
The ``wmi-bmof`` driver exposes this information to userspace, see
Documentation/wmi/devices/wmi-bmof.rst.
In order to retrieve the decoded Binary MOF information, use the following command (requires root):
::
./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof
Sometimes, looking at the disassembled ACPI tables used to describe the WMI device
helps in understanding how the WMI device is supposed to work. The path of the ACPI
method associated with a given WMI device can be retrieved using the ``lswmi`` utility
as mentioned above.
Basic WMI driver structure
--------------------------
The basic WMI driver is build around the struct wmi_driver, which is then bound
to matching WMI devices using a struct wmi_device_id table:
::
static const struct wmi_device_id foo_id_table[] = {
{ "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
{ }
};
MODULE_DEVICE_TABLE(wmi, foo_id_table);
static struct wmi_driver foo_driver = {
.driver = {
.name = "foo",
.probe_type = PROBE_PREFER_ASYNCHRONOUS, /* recommended */
.pm = pm_sleep_ptr(&foo_dev_pm_ops), /* optional */
},
.id_table = foo_id_table,
.probe = foo_probe,
.remove = foo_remove, /* optional, devres is preferred */
.notify = foo_notify, /* optional, for event handling */
.no_notify_data = true, /* optional, enables events containing no additional data */
.no_singleton = true, /* required for new WMI drivers */
};
module_wmi_driver(foo_driver);
The probe() callback is called when the WMI driver is bound to a matching WMI device. Allocating
driver-specific data structures and initialising interfaces to other kernel subsystems should
normally be done in this function.
The remove() callback is then called when the WMI driver is unbound from a WMI device. In order
to unregister interfaces to other kernel subsystems and release resources, devres should be used.
This simplifies error handling during probe and often allows to omit this callback entirely, see
Documentation/driver-api/driver-model/devres.rst for details.
Please note that new WMI drivers are required to be able to be instantiated multiple times,
and are forbidden from using any deprecated GUID-based WMI functions. This means that the
WMI driver should be prepared for the scenario that multiple matching WMI devices are present
on a given machine.
Because of this, WMI drivers should use the state container design pattern as described in
Documentation/driver-api/driver-model/design-patterns.rst.
WMI method drivers
------------------
WMI drivers can call WMI device methods using wmidev_evaluate_method(), the
structure of the ACPI buffer passed to this function is device-specific and usually
needs some tinkering to get right. Looking at the ACPI tables containing the WMI
device usually helps here. The method id and instance number passed to this function
are also device-specific, looking at the decoded Binary MOF is usually enough to
find the right values.
The maximum instance number can be retrieved during runtime using wmidev_instance_count().
Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver.
WMI data block drivers
----------------------
WMI drivers can query WMI device data blocks using wmidev_block_query(), the
structure of the returned ACPI object is again device-specific. Some WMI devices
also allow for setting data blocks using wmidev_block_set().
The maximum instance number can also be retrieved using wmidev_instance_count().
Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example
WMI data block driver.
WMI event drivers
-----------------
WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver.
The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that
the structure of the ACPI object passed to this callback is device-specific, and freeing the
ACPI object is being done by the WMI subsystem, not the driver.
The WMI driver core will take care that the notify() callback will only be called after
the probe() callback has been called, and that no events are being received by the driver
right before and after calling its remove() callback.
However WMI driver developers should be aware that multiple WMI events can be received concurrently,
so any locking (if necessary) needs to be provided by the WMI driver itself.
In order to be able to receive WMI events containing no additional event data,
the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.
Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
Handling multiple WMI devices at once
-------------------------------------
There are many cases of firmware vendors using multiple WMI devices to control different aspects
of a single physical device. This can make developing WMI drivers complicated, as those drivers
might need to communicate with each other to present a unified interface to userspace.
On such case involves a WMI event device which needs to talk to a WMI data block device or WMI
method device upon receiving an WMI event. In such a case, two WMI drivers should be developed,
one for the WMI event device and one for the other WMI device.
The WMI event device driver has only one purpose: to receive WMI events, validate any additional
event data and invoke a notifier chain. The other WMI driver adds itself to this notifier chain
during probing and thus gets notified every time a WMI event is received. This WMI driver might
then process the event further for example by using an input device.
For other WMI device constellations, similar mechanisms can be used.
Things to avoid
---------------
When developing WMI drivers, there are a couple of things which should be avoided:
- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs
- bypassing of the WMI subsystem when talking to WMI devices
- WMI drivers which cannot be instantiated multiple times.
Many older WMI drivers violate one or more points from this list. The reason for
this is that the WMI subsystem evolved significantly over the last two decades,
so there is a lot of legacy cruft inside older WMI drivers.
New WMI drivers are also required to conform to the linux kernel coding style as specified in
Documentation/process/coding-style.rst. The checkpatch utility can catch many common coding style
violations, you can invoke it with the following command:
::
./scripts/checkpatch.pl --strict <path to driver file>
References
==========
.. [1] https://lwn.net/Articles/391230/
......@@ -8,6 +8,7 @@ WMI Subsystem
:maxdepth: 1
acpi-interface
driver-development-guide
devices/index
.. only:: subproject and html
......
......@@ -258,6 +258,12 @@ L: linux-acenic@sunsite.dk
S: Maintained
F: drivers/net/ethernet/alteon/acenic*
ACER ASPIRE 1 EMBEDDED CONTROLLER DRIVER
M: Nikita Travkin <nikita@trvn.ru>
S: Maintained
F: Documentation/devicetree/bindings/platform/acer,aspire1-ec.yaml
F: drivers/platform/arm64/acer-aspire1-ec.c
ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
M: Peter Kaestle <peter@piie.net>
L: platform-driver-x86@vger.kernel.org
......@@ -354,6 +360,12 @@ B: https://bugzilla.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
F: drivers/acpi/pmic/
ACPI QUICKSTART DRIVER
M: Armin Wolf <W_Armin@gmx.de>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/quickstart.c
ACPI SERIAL MULTI INSTANTIATE DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org
......@@ -3110,6 +3122,16 @@ S: Maintained
F: arch/arm64/boot/Makefile
F: scripts/make_fit.py
ARM64 PLATFORM DRIVERS
M: Hans de Goede <hdegoede@redhat.com>
M: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
R: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
L: platform-driver-x86@vger.kernel.org
S: Maintained
Q: https://patchwork.kernel.org/project/platform-driver-x86/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
F: drivers/platform/arm64/
ARM64 PORT (AARCH64 ARCHITECTURE)
M: Catalin Marinas <catalin.marinas@arm.com>
M: Will Deacon <will@kernel.org>
......@@ -5291,7 +5313,6 @@ F: lib/closure.c
CMPC ACPI DRIVER
M: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
M: Daniel Oliveira Nascimento <don@syst.com.br>
L: platform-driver-x86@vger.kernel.org
S: Supported
F: drivers/platform/x86/classmate-laptop.c
......@@ -15191,6 +15212,14 @@ L: platform-driver-x86@vger.kernel.org
S: Orphan
F: drivers/platform/x86/msi-wmi.c
MSI WMI PLATFORM FEATURES
M: Armin Wolf <W_Armin@gmx.de>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/debugfs-msi-wmi-platform
F: Documentation/wmi/devices/msi-wmi-platform.rst
F: drivers/platform/x86/msi-wmi-platform.c
MSI001 MEDIA DRIVER
L: linux-media@vger.kernel.org
S: Orphan
......
......@@ -255,7 +255,25 @@ &i2c2 {
clock-frequency = <400000>;
status = "okay";
/* embedded-controller@76 */
embedded-controller@76 {
compatible = "acer,aspire1-ec";
reg = <0x76>;
interrupts-extended = <&tlmm 30 IRQ_TYPE_LEVEL_LOW>;
pinctrl-0 = <&ec_int_default>;
pinctrl-names = "default";
connector {
compatible = "usb-c-connector";
port {
ec_dp_in: endpoint {
remote-endpoint = <&mdss_dp_out>;
};
};
};
};
};
&i2c4 {
......@@ -419,6 +437,19 @@ &mdss {
status = "okay";
};
&mdss_dp {
data-lanes = <0 1>;
vdda-1p2-supply = <&vreg_l3c_1p2>;
vdda-0p9-supply = <&vreg_l4a_0p8>;
status = "okay";
};
&mdss_dp_out {
remote-endpoint = <&ec_dp_in>;
};
&mdss_dsi0 {
vdda-supply = <&vreg_l3c_1p2>;
status = "okay";
......@@ -857,6 +888,13 @@ codec_irq_default: codec-irq-deault-state {
bias-disable;
};
ec_int_default: ec-int-default-state {
pins = "gpio30";
function = "gpio";
drive-strength = <2>;
bias-disable;
};
edp_bridge_irq_default: edp-bridge-irq-default-state {
pins = "gpio11";
function = "gpio";
......
......@@ -136,6 +136,45 @@ void platform_profile_notify(void)
}
EXPORT_SYMBOL_GPL(platform_profile_notify);
int platform_profile_cycle(void)
{
enum platform_profile_option profile;
enum platform_profile_option next;
int err;
err = mutex_lock_interruptible(&profile_lock);
if (err)
return err;
if (!cur_profile) {
mutex_unlock(&profile_lock);
return -ENODEV;
}
err = cur_profile->profile_get(cur_profile, &profile);
if (err) {
mutex_unlock(&profile_lock);
return err;
}
next = find_next_bit_wrap(cur_profile->choices, PLATFORM_PROFILE_LAST,
profile + 1);
if (WARN_ON(next == PLATFORM_PROFILE_LAST)) {
mutex_unlock(&profile_lock);
return -EINVAL;
}
err = cur_profile->profile_set(cur_profile, next);
mutex_unlock(&profile_lock);
if (!err)
sysfs_notify(acpi_kobj, NULL, "platform_profile");
return err;
}
EXPORT_SYMBOL_GPL(platform_profile_cycle);
int platform_profile_register(struct platform_profile_handler *pprof)
{
int err;
......
......@@ -14,3 +14,5 @@ source "drivers/platform/olpc/Kconfig"
source "drivers/platform/surface/Kconfig"
source "drivers/platform/x86/Kconfig"
source "drivers/platform/arm64/Kconfig"
......@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
obj-$(CONFIG_ARM64) += arm64/
# SPDX-License-Identifier: GPL-2.0-only
#
# EC-like Drivers for aarch64 based devices.
#
menuconfig ARM64_PLATFORM_DEVICES
bool "ARM64 Platform-Specific Device Drivers"
depends on ARM64 || COMPILE_TEST
default y
help
Say Y here to get to see options for platform-specific device drivers
for arm64 based devices, primarily EC-like device drivers.
This option alone does not add any kernel code.
If you say N, all options in this submenu will be skipped and disabled.
if ARM64_PLATFORM_DEVICES
config EC_ACER_ASPIRE1
tristate "Acer Aspire 1 Embedded Controller driver"
depends on I2C
depends on DRM
depends on POWER_SUPPLY
depends on INPUT
help
Say Y here to enable the EC driver for the (Snapdragon-based)
Acer Aspire 1 laptop. The EC handles battery and charging
monitoring as well as some misc functions like the lid sensor
and USB Type-C DP HPD events.
This driver provides battery and AC status support for the mentioned
laptop where this information is not properly exposed via the
standard ACPI devices.
endif # ARM64_PLATFORM_DEVICES
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for linux/drivers/platform/arm64
#
# This dir should only include drivers for EC-like devices.
#
obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
This diff is collapsed.
......@@ -618,15 +618,17 @@ static const struct acpi_gpio_mapping ssam_acpi_gpios[] = {
static int ssam_serial_hub_probe(struct serdev_device *serdev)
{
struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev);
struct device *dev = &serdev->dev;
struct acpi_device *ssh = ACPI_COMPANION(dev);
struct ssam_controller *ctrl;
acpi_status astatus;
int status;
if (gpiod_count(&serdev->dev, NULL) < 0)
return -ENODEV;
status = gpiod_count(dev, NULL);
if (status < 0)
return dev_err_probe(dev, status, "no GPIO found\n");
status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios);
status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios);
if (status)
return status;
......@@ -637,8 +639,10 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
/* Initialize controller. */
status = ssam_controller_init(ctrl, serdev);
if (status)
if (status) {
dev_err_probe(dev, status, "failed to initialize ssam controller\n");
goto err_ctrl_init;
}
ssam_controller_lock(ctrl);
......@@ -646,12 +650,14 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
serdev_device_set_drvdata(serdev, ctrl);
serdev_device_set_client_ops(serdev, &ssam_serdev_ops);
status = serdev_device_open(serdev);
if (status)
if (status) {
dev_err_probe(dev, status, "failed to open serdev device\n");
goto err_devopen;
}
astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
if (ACPI_FAILURE(astatus)) {
status = -ENXIO;
status = dev_err_probe(dev, -ENXIO, "failed to setup serdev\n");
goto err_devinit;
}
......@@ -667,25 +673,33 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
* states.
*/
status = ssam_log_firmware_version(ctrl);
if (status)
if (status) {
dev_err_probe(dev, status, "failed to get firmware version\n");
goto err_initrq;
}
status = ssam_ctrl_notif_d0_entry(ctrl);
if (status)
if (status) {
dev_err_probe(dev, status, "D0-entry notification failed\n");
goto err_initrq;
}
status = ssam_ctrl_notif_display_on(ctrl);
if (status)
if (status) {
dev_err_probe(dev, status, "display-on notification failed\n");
goto err_initrq;
}
status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group);
status = sysfs_create_group(&dev->kobj, &ssam_sam_group);
if (status)
goto err_initrq;
/* Set up IRQ. */
status = ssam_irq_setup(ctrl);
if (status)
if (status) {
dev_err_probe(dev, status, "failed to setup IRQ\n");
goto err_irq;
}
/* Finally, set main controller reference. */
status = ssam_try_set_controller(ctrl);
......@@ -702,7 +716,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
* resumed. In short, this causes some spurious unwanted wake-ups.
* For now let's thus default power/wakeup to false.
*/
device_set_wakeup_capable(&serdev->dev, true);
device_set_wakeup_capable(dev, true);
acpi_dev_clear_dependencies(ssh);
return 0;
......@@ -710,7 +724,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
err_mainref:
ssam_irq_free(ctrl);
err_irq:
sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
sysfs_remove_group(&dev->kobj, &ssam_sam_group);
err_initrq:
ssam_controller_lock(ctrl);
ssam_controller_shutdown(ctrl);
......
......@@ -68,12 +68,32 @@ static const struct software_node ssam_node_bat_sb3base = {
.parent = &ssam_node_hub_base,
};
/* Platform profile / performance-mode device. */
static const struct software_node ssam_node_tmp_pprof = {
/* Platform profile / performance-mode device without a fan. */
static const struct software_node ssam_node_tmp_perf_profile = {
.name = "ssam:01:03:01:00:01",
.parent = &ssam_node_root,
};
/* Platform profile / performance-mode device with a fan, such that
* the fan controller profile can also be switched.
*/
static const struct property_entry ssam_node_tmp_perf_profile_has_fan[] = {
PROPERTY_ENTRY_BOOL("has_fan"),
{ }
};
static const struct software_node ssam_node_tmp_perf_profile_with_fan = {
.name = "ssam:01:03:01:00:01",
.parent = &ssam_node_root,
.properties = ssam_node_tmp_perf_profile_has_fan,
};
/* Thermal sensors. */
static const struct software_node ssam_node_tmp_sensors = {
.name = "ssam:01:03:01:00:02",
.parent = &ssam_node_root,
};
/* Fan speed function. */
static const struct software_node ssam_node_fan_speed = {
.name = "ssam:01:05:01:01:01",
......@@ -208,7 +228,7 @@ static const struct software_node ssam_node_pos_tablet_switch = {
*/
static const struct software_node *ssam_node_group_gen5[] = {
&ssam_node_root,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile,
NULL,
};
......@@ -219,7 +239,7 @@ static const struct software_node *ssam_node_group_sb3[] = {
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_bat_sb3base,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile,
&ssam_node_bas_dtx,
&ssam_node_hid_base_keyboard,
&ssam_node_hid_base_touchpad,
......@@ -233,7 +253,7 @@ 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_tmp_perf_profile,
&ssam_node_hid_main_keyboard,
&ssam_node_hid_main_touchpad,
&ssam_node_hid_main_iid5,
......@@ -245,7 +265,7 @@ static const struct software_node *ssam_node_group_sl5[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile,
&ssam_node_hid_main_keyboard,
&ssam_node_hid_main_touchpad,
&ssam_node_hid_main_iid5,
......@@ -258,7 +278,7 @@ static const struct software_node *ssam_node_group_sls[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile,
&ssam_node_pos_tablet_switch,
&ssam_node_hid_sam_keyboard,
&ssam_node_hid_sam_penstash,
......@@ -274,7 +294,7 @@ static const struct software_node *ssam_node_group_slg1[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile,
NULL,
};
......@@ -283,7 +303,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,
&ssam_node_tmp_perf_profile,
NULL,
};
......@@ -293,7 +313,7 @@ static const struct software_node *ssam_node_group_sp8[] = {
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile,
&ssam_node_kip_tablet_switch,
&ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash,
......@@ -310,7 +330,8 @@ static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_tmp_pprof,
&ssam_node_tmp_perf_profile_with_fan,
&ssam_node_tmp_sensors,
&ssam_node_fan_speed,
&ssam_node_pos_tablet_switch,
&ssam_node_hid_kip_keyboard,
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Surface Platform Profile / Performance Mode driver for Surface System
* Aggregator Module (thermal subsystem).
* Aggregator Module (thermal and fan subsystem).
*
* Copyright (C) 2021-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
......@@ -14,6 +14,7 @@
#include <linux/surface_aggregator/device.h>
// Enum for the platform performance profile sent to the TMP module.
enum ssam_tmp_profile {
SSAM_TMP_PROFILE_NORMAL = 1,
SSAM_TMP_PROFILE_BATTERY_SAVER = 2,
......@@ -21,15 +22,26 @@ enum ssam_tmp_profile {
SSAM_TMP_PROFILE_BEST_PERFORMANCE = 4,
};
// Enum for the fan profile sent to the FAN module. This fan profile is
// only sent to the EC if the 'has_fan' property is set. The integers are
// not a typo, they differ from the performance profile indices.
enum ssam_fan_profile {
SSAM_FAN_PROFILE_NORMAL = 2,
SSAM_FAN_PROFILE_BATTERY_SAVER = 1,
SSAM_FAN_PROFILE_BETTER_PERFORMANCE = 3,
SSAM_FAN_PROFILE_BEST_PERFORMANCE = 4,
};
struct ssam_tmp_profile_info {
__le32 profile;
__le16 unknown1;
__le16 unknown2;
} __packed;
struct ssam_tmp_profile_device {
struct ssam_platform_profile_device {
struct ssam_device *sdev;
struct platform_profile_handler handler;
bool has_fan;
};
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {
......@@ -42,6 +54,13 @@ SSAM_DEFINE_SYNC_REQUEST_CL_W(__ssam_tmp_profile_set, __le32, {
.command_id = 0x03,
});
SSAM_DEFINE_SYNC_REQUEST_W(__ssam_fan_profile_set, u8, {
.target_category = SSAM_SSH_TC_FAN,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x0e,
.instance_id = 0x01,
});
static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile *p)
{
struct ssam_tmp_profile_info info;
......@@ -57,12 +76,19 @@ static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile
static int ssam_tmp_profile_set(struct ssam_device *sdev, enum ssam_tmp_profile p)
{
__le32 profile_le = cpu_to_le32(p);
const __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)
static int ssam_fan_profile_set(struct ssam_device *sdev, enum ssam_fan_profile p)
{
const u8 profile = p;
return ssam_retry(__ssam_fan_profile_set, sdev->ctrl, &profile);
}
static int convert_ssam_tmp_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p)
{
switch (p) {
case SSAM_TMP_PROFILE_NORMAL:
......@@ -83,7 +109,8 @@ static int convert_ssam_to_profile(struct ssam_device *sdev, enum ssam_tmp_profi
}
}
static int convert_profile_to_ssam(struct ssam_device *sdev, enum platform_profile_option p)
static int convert_profile_to_ssam_tmp(struct ssam_device *sdev, enum platform_profile_option p)
{
switch (p) {
case PLATFORM_PROFILE_LOW_POWER:
......@@ -105,20 +132,42 @@ static int convert_profile_to_ssam(struct ssam_device *sdev, enum platform_profi
}
}
static int convert_profile_to_ssam_fan(struct ssam_device *sdev, enum platform_profile_option p)
{
switch (p) {
case PLATFORM_PROFILE_LOW_POWER:
return SSAM_FAN_PROFILE_BATTERY_SAVER;
case PLATFORM_PROFILE_BALANCED:
return SSAM_FAN_PROFILE_NORMAL;
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
return SSAM_FAN_PROFILE_BETTER_PERFORMANCE;
case PLATFORM_PROFILE_PERFORMANCE:
return SSAM_FAN_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;
struct ssam_platform_profile_device *tpd;
enum ssam_tmp_profile tp;
int status;
tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
status = ssam_tmp_profile_get(tpd->sdev, &tp);
if (status)
return status;
status = convert_ssam_to_profile(tpd->sdev, tp);
status = convert_ssam_tmp_to_profile(tpd->sdev, tp);
if (status < 0)
return status;
......@@ -129,21 +178,32 @@ static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
struct ssam_tmp_profile_device *tpd;
struct ssam_platform_profile_device *tpd;
int tp;
tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
tp = convert_profile_to_ssam_tmp(tpd->sdev, profile);
if (tp < 0)
return tp;
tp = convert_profile_to_ssam(tpd->sdev, profile);
tp = ssam_tmp_profile_set(tpd->sdev, tp);
if (tp < 0)
return tp;
return ssam_tmp_profile_set(tpd->sdev, tp);
if (tpd->has_fan) {
tp = convert_profile_to_ssam_fan(tpd->sdev, profile);
if (tp < 0)
return tp;
tp = ssam_fan_profile_set(tpd->sdev, tp);
}
return tp;
}
static int surface_platform_profile_probe(struct ssam_device *sdev)
{
struct ssam_tmp_profile_device *tpd;
struct ssam_platform_profile_device *tpd;
tpd = devm_kzalloc(&sdev->dev, sizeof(*tpd), GFP_KERNEL);
if (!tpd)
......@@ -154,6 +214,8 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
tpd->handler.profile_get = ssam_platform_profile_get;
tpd->handler.profile_set = ssam_platform_profile_set;
tpd->has_fan = device_property_read_bool(&sdev->dev, "has_fan");
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);
......
......@@ -133,6 +133,17 @@ config YOGABOOK
To compile this driver as a module, choose M here: the module will
be called lenovo-yogabook.
config YT2_1380
tristate "Lenovo Yoga Tablet 2 1380 fast charge driver"
depends on SERIAL_DEV_BUS
depends on ACPI
help
Say Y here to enable support for the custom fast charging protocol
found on the Lenovo Yoga Tablet 2 1380F / 1380L models.
To compile this driver as a module, choose M here: the module will
be called lenovo-yogabook.
config ACERHDF
tristate "Acer Aspire One temperature and fan driver"
depends on ACPI && THERMAL
......@@ -642,6 +653,30 @@ config THINKPAD_LMI
source "drivers/platform/x86/intel/Kconfig"
config ACPI_QUICKSTART
tristate "ACPI Quickstart button driver"
depends on ACPI
depends on INPUT
select INPUT_SPARSEKMAP
help
This driver adds support for ACPI quickstart button (PNP0C32) devices.
The button emits a manufacturer-specific key value when pressed, so
userspace has to map this value to a standard key code.
To compile this driver as a module, choose M here: the module will be
called quickstart.
config MEEGOPAD_ANX7428
tristate "MeeGoPad ANX7428 Type-C Switch"
depends on ACPI && GPIOLIB && I2C
help
Some MeeGoPad top-set boxes have an ANX7428 Type-C Switch for
USB3.1 Gen 1 and DisplayPort over Type-C alternate mode support.
This driver takes care of powering on the ANX7428 on supported
MeeGoPad top-set boxes. After this the ANX7428 takes care of Type-C
connector orientation and PD alternate mode switching autonomously.
config MSI_EC
tristate "MSI EC Extras"
depends on ACPI
......@@ -685,6 +720,17 @@ config MSI_WMI
To compile this driver as a module, choose M here: the module will
be called msi-wmi.
config MSI_WMI_PLATFORM
tristate "MSI WMI Platform features"
depends on ACPI_WMI
depends on HWMON
help
Say Y here if you want to have support for WMI-based platform features
like fan sensor access on MSI machines.
To compile this driver as a module, choose M here: the module will
be called msi-wmi-platform.
config XO15_EBOOK
tristate "OLPC XO-1.5 ebook switch"
depends on OLPC || COMPILE_TEST
......@@ -996,6 +1042,18 @@ config INSPUR_PLATFORM_PROFILE
To compile this driver as a module, choose M here: the module
will be called inspur-platform-profile.
config LENOVO_WMI_CAMERA
tristate "Lenovo WMI Camera Button driver"
depends on ACPI_WMI
depends on INPUT
help
This driver provides support for Lenovo camera button. The Camera
button is a GPIO device. This driver receives ACPI notifications when
the camera button is switched on/off.
To compile this driver as a module, choose M here: the module
will be called lenovo-wmi-camera.
source "drivers/platform/x86/x86-android-tablets/Kconfig"
config FW_ATTR_CLASS
......
......@@ -66,14 +66,23 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
obj-$(CONFIG_YT2_1380) += lenovo-yoga-tab2-pro-1380-fastcharger.o
obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o
# Intel
obj-y += intel/
# Microsoft
obj-$(CONFIG_ACPI_QUICKSTART) += quickstart.o
# MeeGoPad
obj-$(CONFIG_MEEGOPAD_ANX7428) += meegopad_anx7428.o
# MSI
obj-$(CONFIG_MSI_EC) += msi-ec.o
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
obj-$(CONFIG_MSI_WMI_PLATFORM) += msi-wmi-platform.o
# OLPC
obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o
......
......@@ -693,7 +693,7 @@ static int hsmp_create_non_acpi_sysfs_if(struct device *dev)
hsmp_create_attr_list(attr_grp, dev, i);
}
return devm_device_add_groups(dev, hsmp_attr_grps);
return device_add_groups(dev, hsmp_attr_grps);
}
static int hsmp_create_acpi_sysfs_if(struct device *dev)
......
......@@ -18,3 +18,18 @@ config AMD_PMC
If you choose to compile this driver as a module the module will be
called amd-pmc.
config AMD_MP2_STB
bool "AMD SoC MP2 STB function"
depends on AMD_PMC
default AMD_PMC
help
AMD MP2 STB function provides a data buffer used to log debug
information about the system execution during S2Idle suspend/resume.
A data buffer known as the STB (Smart Trace Buffer) is a circular
buffer which is a low-level log for the SoC which is used to debug
any hangs/stalls during S2Idle suspend/resume.
Creates debugfs to get STB, a userspace daemon can access STB log of
last S2Idle suspend/resume which can help to debug if hangs/stalls
during S2Idle suspend/resume.
......@@ -6,3 +6,4 @@
amd-pmc-objs := pmc.o pmc-quirks.o
obj-$(CONFIG_AMD_PMC) += amd-pmc.o
amd-pmc-$(CONFIG_AMD_MP2_STB) += mp2_stb.o
// SPDX-License-Identifier: GPL-2.0
/*
* AMD MP2 STB layer
*
* Copyright (c) 2024, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <linux/sizes.h>
#include <linux/time.h>
#include "pmc.h"
#define VALID_MSG 0xA
#define VALID_RESPONSE 2
#define AMD_C2P_MSG0 0x10500
#define AMD_C2P_MSG1 0x10504
#define AMD_P2C_MSG0 0x10680
#define AMD_P2C_MSG1 0x10684
#define MP2_RESP_SLEEP_US 500
#define MP2_RESP_TIMEOUT_US (1600 * USEC_PER_MSEC)
#define MP2_STB_DATA_LEN_2KB 1
#define MP2_STB_DATA_LEN_16KB 4
#define MP2_MMIO_BAR 2
struct mp2_cmd_base {
union {
u32 ul;
struct {
u32 cmd_id : 4;
u32 intr_disable : 1;
u32 is_dma_used : 1;
u32 rsvd : 26;
} field;
};
};
struct mp2_cmd_response {
union {
u32 resp;
struct {
u32 cmd_id : 4;
u32 status : 4;
u32 response : 4;
u32 rsvd2 : 20;
} field;
};
};
struct mp2_stb_data_valid {
union {
u32 data_valid;
struct {
u32 valid : 16;
u32 length : 16;
} val;
};
};
static int amd_mp2_wait_response(struct amd_mp2_dev *mp2, u8 cmd_id, u32 command_sts)
{
struct mp2_cmd_response cmd_resp;
if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG0, cmd_resp.resp,
(cmd_resp.field.response == 0x0 &&
cmd_resp.field.status == command_sts &&
cmd_resp.field.cmd_id == cmd_id), MP2_RESP_SLEEP_US,
MP2_RESP_TIMEOUT_US))
return cmd_resp.field.status;
return -ETIMEDOUT;
}
static void amd_mp2_stb_send_cmd(struct amd_mp2_dev *mp2, u8 cmd_id, bool is_dma_used)
{
struct mp2_cmd_base cmd_base;
cmd_base.ul = 0;
cmd_base.field.cmd_id = cmd_id;
cmd_base.field.intr_disable = 1;
cmd_base.field.is_dma_used = is_dma_used;
writeq(mp2->dma_addr, mp2->mmio + AMD_C2P_MSG1);
writel(cmd_base.ul, mp2->mmio + AMD_C2P_MSG0);
}
static int amd_mp2_stb_region(struct amd_mp2_dev *mp2)
{
struct device *dev = &mp2->pdev->dev;
unsigned int len = mp2->stb_len;
if (!mp2->stbdata) {
mp2->vslbase = dmam_alloc_coherent(dev, len, &mp2->dma_addr, GFP_KERNEL);
if (!mp2->vslbase)
return -ENOMEM;
mp2->stbdata = devm_kzalloc(dev, len, GFP_KERNEL);
if (!mp2->stbdata)
return -ENOMEM;
}
return 0;
}
static int amd_mp2_process_cmd(struct amd_mp2_dev *mp2, struct file *filp)
{
struct device *dev = &mp2->pdev->dev;
struct mp2_stb_data_valid stb_dv;
int status;
stb_dv.data_valid = readl(mp2->mmio + AMD_P2C_MSG1);
if (stb_dv.val.valid != VALID_MSG) {
dev_dbg(dev, "Invalid STB data\n");
return -EBADMSG;
}
if (stb_dv.val.length != MP2_STB_DATA_LEN_2KB &&
stb_dv.val.length != MP2_STB_DATA_LEN_16KB) {
dev_dbg(dev, "Unsupported length\n");
return -EMSGSIZE;
}
mp2->stb_len = BIT(stb_dv.val.length) * SZ_1K;
status = amd_mp2_stb_region(mp2);
if (status) {
dev_err(dev, "Failed to init STB region, status %d\n", status);
return status;
}
amd_mp2_stb_send_cmd(mp2, VALID_MSG, true);
status = amd_mp2_wait_response(mp2, VALID_MSG, VALID_RESPONSE);
if (status == VALID_RESPONSE) {
memcpy_fromio(mp2->stbdata, mp2->vslbase, mp2->stb_len);
filp->private_data = mp2->stbdata;
mp2->is_stb_data = true;
} else {
dev_err(dev, "Failed to start STB dump, status %d\n", status);
return -EOPNOTSUPP;
}
return 0;
}
static int amd_mp2_stb_debugfs_open(struct inode *inode, struct file *filp)
{
struct amd_pmc_dev *dev = filp->f_inode->i_private;
struct amd_mp2_dev *mp2 = dev->mp2;
if (mp2) {
if (!mp2->is_stb_data)
return amd_mp2_process_cmd(mp2, filp);
filp->private_data = mp2->stbdata;
return 0;
}
return -ENODEV;
}
static ssize_t amd_mp2_stb_debugfs_read(struct file *filp, char __user *buf, size_t size,
loff_t *pos)
{
struct amd_pmc_dev *dev = filp->f_inode->i_private;
struct amd_mp2_dev *mp2 = dev->mp2;
if (!mp2)
return -ENODEV;
if (!filp->private_data)
return -EINVAL;
return simple_read_from_buffer(buf, size, pos, filp->private_data, mp2->stb_len);
}
static const struct file_operations amd_mp2_stb_debugfs_fops = {
.owner = THIS_MODULE,
.open = amd_mp2_stb_debugfs_open,
.read = amd_mp2_stb_debugfs_read,
};
static void amd_mp2_dbgfs_register(struct amd_pmc_dev *dev)
{
if (!dev->dbgfs_dir)
return;
debugfs_create_file("stb_read_previous_boot", 0644, dev->dbgfs_dir, dev,
&amd_mp2_stb_debugfs_fops);
}
void amd_mp2_stb_deinit(struct amd_pmc_dev *dev)
{
struct amd_mp2_dev *mp2 = dev->mp2;
struct pci_dev *pdev;
if (mp2 && mp2->pdev) {
pdev = mp2->pdev;
if (mp2->mmio)
pci_clear_master(pdev);
pci_dev_put(pdev);
if (mp2->devres_gid)
devres_release_group(&pdev->dev, mp2->devres_gid);
dev->mp2 = NULL;
}
}
void amd_mp2_stb_init(struct amd_pmc_dev *dev)
{
struct amd_mp2_dev *mp2 = NULL;
struct pci_dev *pdev;
int rc;
mp2 = devm_kzalloc(dev->dev, sizeof(*mp2), GFP_KERNEL);
if (!mp2)
return;
pdev = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MP2_STB, NULL);
if (!pdev)
return;
dev->mp2 = mp2;
mp2->pdev = pdev;
mp2->devres_gid = devres_open_group(&pdev->dev, NULL, GFP_KERNEL);
if (!mp2->devres_gid) {
dev_err(&pdev->dev, "devres_open_group failed\n");
goto mp2_error;
}
rc = pcim_enable_device(pdev);
if (rc) {
dev_err(&pdev->dev, "pcim_enable_device failed\n");
goto mp2_error;
}
rc = pcim_iomap_regions(pdev, BIT(MP2_MMIO_BAR), "mp2 stb");
if (rc) {
dev_err(&pdev->dev, "pcim_iomap_regions failed\n");
goto mp2_error;
}
mp2->mmio = pcim_iomap_table(pdev)[MP2_MMIO_BAR];
if (!mp2->mmio) {
dev_err(&pdev->dev, "pcim_iomap_table failed\n");
goto mp2_error;
}
pci_set_master(pdev);
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc) {
dev_err(&pdev->dev, "failed to set DMA mask\n");
goto mp2_error;
}
amd_mp2_dbgfs_register(dev);
return;
mp2_error:
amd_mp2_stb_deinit(dev);
}
......@@ -1106,6 +1106,8 @@ static int amd_pmc_probe(struct platform_device *pdev)
}
amd_pmc_dbgfs_register(dev);
if (IS_ENABLED(CONFIG_AMD_MP2_STB))
amd_mp2_stb_init(dev);
pm_report_max_hw_sleep(U64_MAX);
return 0;
......@@ -1122,6 +1124,8 @@ static void amd_pmc_remove(struct platform_device *pdev)
acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
amd_pmc_dbgfs_unregister(dev);
pci_dev_put(dev->rdev);
if (IS_ENABLED(CONFIG_AMD_MP2_STB))
amd_mp2_stb_deinit(dev);
mutex_destroy(&dev->lock);
}
......@@ -1132,6 +1136,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = {
{"AMDI0008", 0},
{"AMDI0009", 0},
{"AMDI000A", 0},
{"AMDI000B", 0},
{"AMD0004", 0},
{"AMD0005", 0},
{ }
......
......@@ -14,6 +14,17 @@
#include <linux/types.h>
#include <linux/mutex.h>
struct amd_mp2_dev {
void __iomem *mmio;
void __iomem *vslbase;
void *stbdata;
void *devres_gid;
struct pci_dev *pdev;
dma_addr_t dma_addr;
int stb_len;
bool is_stb_data;
};
struct amd_pmc_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
......@@ -38,10 +49,13 @@ struct amd_pmc_dev {
struct dentry *dbgfs_dir;
struct quirk_entry *quirks;
bool disable_8042_wakeup;
struct amd_mp2_dev *mp2;
};
void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev);
void amd_pmc_quirks_init(struct amd_pmc_dev *dev);
void amd_mp2_stb_init(struct amd_pmc_dev *dev);
void amd_mp2_stb_deinit(struct amd_pmc_dev *dev);
/* List of supported CPU ids */
#define AMD_CPU_ID_RV 0x15D0
......@@ -53,5 +67,6 @@ void amd_pmc_quirks_init(struct amd_pmc_dev *dev);
#define AMD_CPU_ID_PS 0x14E8
#define AMD_CPU_ID_SP 0x14A4
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c
#endif /* PMC_H */
......@@ -381,6 +381,7 @@ static const struct acpi_device_id amd_pmf_acpi_ids[] = {
{"AMDI0100", 0x100},
{"AMDI0102", 0},
{"AMDI0103", 0},
{"AMDI0105", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
......
......@@ -852,8 +852,8 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
* so we don't set eof to 1
*/
len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
len += sprintf(page + len, "Model reference : %s\n", asus->name);
len += sysfs_emit_at(page, len, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
len += sysfs_emit_at(page, len, "Model reference : %s\n", asus->name);
/*
* The SFUN method probably allows the original driver to get the list
* of features supported by a given model. For now, 0x0100 or 0x0800
......@@ -862,7 +862,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
*/
rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp);
if (ACPI_SUCCESS(rv))
len += sprintf(page + len, "SFUN value : %#x\n",
len += sysfs_emit_at(page, len, "SFUN value : %#x\n",
(uint) temp);
/*
* The HWRS method return informations about the hardware.
......@@ -874,7 +874,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
*/
rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);
if (ACPI_SUCCESS(rv))
len += sprintf(page + len, "HWRS value : %#x\n",
len += sysfs_emit_at(page, len, "HWRS value : %#x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
......@@ -885,25 +885,25 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
*/
rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp);
if (ACPI_SUCCESS(rv))
len += sprintf(page + len, "ASYM value : %#x\n",
len += sysfs_emit_at(page, len, "ASYM value : %#x\n",
(uint) temp);
if (asus->dsdt_info) {
snprintf(buf, 16, "%d", asus->dsdt_info->length);
len += sprintf(page + len, "DSDT length : %s\n", buf);
len += sysfs_emit_at(page, len, "DSDT length : %s\n", buf);
snprintf(buf, 16, "%d", asus->dsdt_info->checksum);
len += sprintf(page + len, "DSDT checksum : %s\n", buf);
len += sysfs_emit_at(page, len, "DSDT checksum : %s\n", buf);
snprintf(buf, 16, "%d", asus->dsdt_info->revision);
len += sprintf(page + len, "DSDT revision : %s\n", buf);
len += sysfs_emit_at(page, len, "DSDT revision : %s\n", buf);
snprintf(buf, 7, "%s", asus->dsdt_info->oem_id);
len += sprintf(page + len, "OEM id : %s\n", buf);
len += sysfs_emit_at(page, len, "OEM id : %s\n", buf);
snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id);
len += sprintf(page + len, "OEM table id : %s\n", buf);
len += sysfs_emit_at(page, len, "OEM table id : %s\n", buf);
snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision);
len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
len += sysfs_emit_at(page, len, "OEM revision : 0x%s\n", buf);
snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id);
len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
len += sysfs_emit_at(page, len, "ASL comp vendor id : %s\n", buf);
snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision);
len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
len += sysfs_emit_at(page, len, "ASL comp revision : 0x%s\n", buf);
}
return len;
......@@ -933,7 +933,7 @@ static ssize_t ledd_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "0x%08x\n", asus->ledd_status);
return sysfs_emit(buf, "0x%08x\n", asus->ledd_status);
}
static ssize_t ledd_store(struct device *dev, struct device_attribute *attr,
......@@ -993,7 +993,7 @@ static ssize_t wlan_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
}
static ssize_t wlan_store(struct device *dev, struct device_attribute *attr,
......@@ -1022,7 +1022,7 @@ static ssize_t bluetooth_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
}
static ssize_t bluetooth_store(struct device *dev,
......@@ -1052,7 +1052,7 @@ static ssize_t wimax_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus_wireless_status(asus, WM_RSTS));
return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WM_RSTS));
}
static ssize_t wimax_store(struct device *dev, struct device_attribute *attr,
......@@ -1081,7 +1081,7 @@ static ssize_t wwan_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus_wireless_status(asus, WW_RSTS));
return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WW_RSTS));
}
static ssize_t wwan_store(struct device *dev, struct device_attribute *attr,
......@@ -1151,7 +1151,7 @@ static ssize_t ls_switch_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus->light_switch);
return sysfs_emit(buf, "%d\n", asus->light_switch);
}
static ssize_t ls_switch_store(struct device *dev,
......@@ -1182,7 +1182,7 @@ static ssize_t ls_level_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus->light_level);
return sysfs_emit(buf, "%d\n", asus->light_level);
}
static ssize_t ls_level_store(struct device *dev, struct device_attribute *attr,
......@@ -1228,7 +1228,7 @@ static ssize_t ls_value_show(struct device *dev, struct device_attribute *attr,
if (!err)
err = pega_int_read(asus, PEGA_READ_ALS_L, &lo);
if (!err)
return sprintf(buf, "%d\n", 10 * hi + lo);
return sysfs_emit(buf, "%d\n", 10 * hi + lo);
return err;
}
static DEVICE_ATTR_RO(ls_value);
......@@ -1264,7 +1264,7 @@ static ssize_t gps_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", asus_gps_status(asus));
return sysfs_emit(buf, "%d\n", asus_gps_status(asus));
}
static ssize_t gps_store(struct device *dev, struct device_attribute *attr,
......
This diff is collapsed.
......@@ -13,8 +13,6 @@
#include <linux/input.h>
#include <linux/rfkill.h>
MODULE_LICENSE("GPL");
struct cmpc_accel {
int sensitivity;
int g_select;
......@@ -1139,3 +1137,5 @@ static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
};
MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");
MODULE_LICENSE("GPL");
......@@ -145,6 +145,21 @@ config DELL_SMO8800
To compile this driver as a module, choose M here: the module will
be called dell-smo8800.
config DELL_UART_BACKLIGHT
tristate "Dell AIO UART Backlight driver"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIAL_DEV_BUS
help
Say Y here if you want to support Dell AIO UART backlight interface.
The Dell AIO machines released after 2017 come with a UART interface
to communicate with the backlight scalar board. This driver creates
a standard backlight interface and talks to the scalar board through
UART to adjust the AIO screen brightness.
To compile this driver as a module, choose M here: the module will
be called dell_uart_backlight.
config DELL_WMI
tristate "Dell WMI notifications"
default m
......
......@@ -14,6 +14,7 @@ dell-smbios-objs := dell-smbios-base.o
dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o
dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
obj-$(CONFIG_DELL_UART_BACKLIGHT) += dell-uart-backlight.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
dell-wmi-objs := dell-wmi-base.o
dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o
......
This diff is collapsed.
......@@ -386,11 +386,11 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_LID))
return sprintf(buf, "unknown\n");
return sysfs_emit(buf, "unknown\n");
if (priv->flags_state & FLAG_LID)
return sprintf(buf, "open\n");
return sysfs_emit(buf, "open\n");
else
return sprintf(buf, "closed\n");
return sysfs_emit(buf, "closed\n");
}
static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
......@@ -399,11 +399,11 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_DOCK))
return sprintf(buf, "unknown\n");
return sysfs_emit(buf, "unknown\n");
if (priv->flags_state & FLAG_DOCK)
return sprintf(buf, "docked\n");
return sysfs_emit(buf, "docked\n");
else
return sprintf(buf, "undocked\n");
return sysfs_emit(buf, "undocked\n");
}
static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
......@@ -412,11 +412,11 @@ static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_RFKILL))
return sprintf(buf, "unknown\n");
return sysfs_emit(buf, "unknown\n");
if (priv->flags_state & FLAG_RFKILL)
return sprintf(buf, "on\n");
return sysfs_emit(buf, "on\n");
else
return sprintf(buf, "killed\n");
return sysfs_emit(buf, "killed\n");
}
static DEVICE_ATTR_RO(lid);
......
......@@ -681,7 +681,7 @@ static ssize_t display_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr,
......@@ -691,7 +691,7 @@ static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t als_show(struct device *dev, struct device_attribute *attr,
......@@ -701,7 +701,7 @@ static ssize_t als_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
......@@ -711,7 +711,7 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t tablet_show(struct device *dev, struct device_attribute *attr,
......@@ -721,7 +721,7 @@ static ssize_t tablet_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
......@@ -732,7 +732,7 @@ static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
return sprintf(buf, "0x%x\n", value);
return sysfs_emit(buf, "0x%x\n", value);
}
static ssize_t als_store(struct device *dev, struct device_attribute *attr,
......
......@@ -379,7 +379,7 @@ static ssize_t charge_control_start_threshold_show(struct device *dev,
if (err)
return err;
return sprintf(buf, "%d\n", start);
return sysfs_emit(buf, "%d\n", start);
}
static ssize_t charge_control_end_threshold_show(struct device *dev,
......@@ -392,7 +392,7 @@ static ssize_t charge_control_end_threshold_show(struct device *dev,
if (err)
return err;
return sprintf(buf, "%d\n", end);
return sysfs_emit(buf, "%d\n", end);
}
static ssize_t charge_control_thresholds_show(struct device *dev,
......@@ -405,7 +405,7 @@ static ssize_t charge_control_thresholds_show(struct device *dev,
if (err)
return err;
return sprintf(buf, "%d %d\n", start, end);
return sysfs_emit(buf, "%d %d\n", start, end);
}
static ssize_t charge_control_start_threshold_store(struct device *dev,
......@@ -562,7 +562,7 @@ static ssize_t fn_lock_state_show(struct device *dev,
if (err)
return err;
return sprintf(buf, "%d\n", on);
return sysfs_emit(buf, "%d\n", on);
}
static ssize_t fn_lock_state_store(struct device *dev,
......
......@@ -152,6 +152,11 @@ struct ideapad_private {
struct led_classdev led;
unsigned int last_brightness;
} kbd_bl;
struct {
bool initialized;
struct led_classdev led;
unsigned int last_brightness;
} fn_lock;
};
static bool no_bt_rfkill;
......@@ -513,11 +518,8 @@ static ssize_t fan_mode_store(struct device *dev,
static DEVICE_ATTR_RW(fan_mode);
static ssize_t fn_lock_show(struct device *dev,
struct device_attribute *attr,
char *buf)
static int ideapad_fn_lock_get(struct ideapad_private *priv)
{
struct ideapad_private *priv = dev_get_drvdata(dev);
unsigned long hals;
int err;
......@@ -525,7 +527,40 @@ static ssize_t fn_lock_show(struct device *dev,
if (err)
return err;
return sysfs_emit(buf, "%d\n", !!test_bit(HALS_FNLOCK_STATE_BIT, &hals));
return !!test_bit(HALS_FNLOCK_STATE_BIT, &hals);
}
static int ideapad_fn_lock_set(struct ideapad_private *priv, bool state)
{
return exec_sals(priv->adev->handle,
state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
}
static void ideapad_fn_lock_led_notify(struct ideapad_private *priv, int brightness)
{
if (!priv->fn_lock.initialized)
return;
if (brightness == priv->fn_lock.last_brightness)
return;
priv->fn_lock.last_brightness = brightness;
led_classdev_notify_brightness_hw_changed(&priv->fn_lock.led, brightness);
}
static ssize_t fn_lock_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ideapad_private *priv = dev_get_drvdata(dev);
int brightness;
brightness = ideapad_fn_lock_get(priv);
if (brightness < 0)
return brightness;
return sysfs_emit(buf, "%d\n", brightness);
}
static ssize_t fn_lock_store(struct device *dev,
......@@ -540,10 +575,12 @@ static ssize_t fn_lock_store(struct device *dev,
if (err)
return err;
err = exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
err = ideapad_fn_lock_set(priv, state);
if (err)
return err;
ideapad_fn_lock_led_notify(priv, state);
return count;
}
......@@ -1181,8 +1218,11 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)
switch (bit) {
case 6: /* Z570 */
case 0: /* Z580 */
/* Thermal Management button */
ideapad_input_report(priv, 65);
/* Thermal Management / Performance Mode button */
if (priv->dytc)
platform_profile_cycle();
else
ideapad_input_report(priv, 65);
break;
case 1:
/* OneKey Theater button */
......@@ -1462,6 +1502,65 @@ static void ideapad_kbd_bl_exit(struct ideapad_private *priv)
led_classdev_unregister(&priv->kbd_bl.led);
}
/*
* FnLock LED
*/
static enum led_brightness ideapad_fn_lock_led_cdev_get(struct led_classdev *led_cdev)
{
struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);
return ideapad_fn_lock_get(priv);
}
static int ideapad_fn_lock_led_cdev_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);
return ideapad_fn_lock_set(priv, brightness);
}
static int ideapad_fn_lock_led_init(struct ideapad_private *priv)
{
int brightness, err;
if (!priv->features.fn_lock)
return -ENODEV;
if (WARN_ON(priv->fn_lock.initialized))
return -EEXIST;
priv->fn_lock.led.max_brightness = 1;
brightness = ideapad_fn_lock_get(priv);
if (brightness < 0)
return brightness;
priv->fn_lock.last_brightness = brightness;
priv->fn_lock.led.name = "platform::" LED_FUNCTION_FNLOCK;
priv->fn_lock.led.brightness_get = ideapad_fn_lock_led_cdev_get;
priv->fn_lock.led.brightness_set_blocking = ideapad_fn_lock_led_cdev_set;
priv->fn_lock.led.flags = LED_BRIGHT_HW_CHANGED;
err = led_classdev_register(&priv->platform_device->dev, &priv->fn_lock.led);
if (err)
return err;
priv->fn_lock.initialized = true;
return 0;
}
static void ideapad_fn_lock_led_exit(struct ideapad_private *priv)
{
if (!priv->fn_lock.initialized)
return;
priv->fn_lock.initialized = false;
led_classdev_unregister(&priv->fn_lock.led);
}
/*
* module init/exit
*/
......@@ -1709,7 +1808,6 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
{
struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev);
struct ideapad_private *priv;
unsigned long result;
mutex_lock(&ideapad_shared_mutex);
......@@ -1722,11 +1820,13 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
ideapad_input_report(priv, 128);
break;
case IDEAPAD_WMI_EVENT_FN_KEYS:
if (priv->features.set_fn_lock_led &&
!eval_hals(priv->adev->handle, &result)) {
bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result);
if (priv->features.set_fn_lock_led) {
int brightness = ideapad_fn_lock_get(priv);
exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
if (brightness >= 0) {
ideapad_fn_lock_set(priv, brightness);
ideapad_fn_lock_led_notify(priv, brightness);
}
}
if (data->type != ACPI_TYPE_INTEGER) {
......@@ -1738,6 +1838,10 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n",
data->integer.value);
/* 0x02 FnLock, 0x03 Esc */
if (data->integer.value == 0x02 || data->integer.value == 0x03)
ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
ideapad_input_report(priv,
data->integer.value | IDEAPAD_WMI_KEY);
......@@ -1831,6 +1935,14 @@ static int ideapad_acpi_add(struct platform_device *pdev)
dev_info(&pdev->dev, "Keyboard backlight control not available\n");
}
err = ideapad_fn_lock_led_init(priv);
if (err) {
if (err != -ENODEV)
dev_warn(&pdev->dev, "Could not set up FnLock LED: %d\n", err);
else
dev_info(&pdev->dev, "FnLock control not available\n");
}
/*
* On some models without a hw-switch (the yoga 2 13 at least)
* VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
......@@ -1887,6 +1999,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(priv, i);
ideapad_fn_lock_led_exit(priv);
ideapad_kbd_bl_exit(priv);
ideapad_input_exit(priv);
......@@ -1914,6 +2027,7 @@ static void ideapad_acpi_remove(struct platform_device *pdev)
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(priv, i);
ideapad_fn_lock_led_exit(priv);
ideapad_kbd_bl_exit(priv);
ideapad_input_exit(priv);
ideapad_debugfs_exit(priv);
......
......@@ -207,6 +207,7 @@ static struct wmi_driver inspur_wmi_driver = {
.id_table = inspur_wmi_id_table,
.probe = inspur_wmi_probe,
.remove = inspur_wmi_remove,
.no_singleton = true,
};
module_wmi_driver(inspur_wmi_driver);
......
......@@ -233,7 +233,9 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
chunk_table[0] = starting_chunk_nr + i;
chunk_table[1] = linear_addr;
do {
local_irq_disable();
wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table);
local_irq_enable();
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data);
err_code = chunk_status.error_code;
} while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count);
......
......@@ -69,6 +69,19 @@ static const char * const scan_test_status[] = {
static void message_not_tested(struct device *dev, int cpu, union ifs_status status)
{
struct ifs_data *ifsd = ifs_get_data(dev);
/*
* control_error is set when the microcode runs into a problem
* loading the image from the reserved BIOS memory, or it has
* been corrupted. Reloading the image may fix this issue.
*/
if (status.control_error) {
dev_warn(dev, "CPU(s) %*pbl: Scan controller error. Batch: %02x version: 0x%x\n",
cpumask_pr_args(cpu_smt_mask(cpu)), ifsd->cur_batch, ifsd->loaded_version);
return;
}
if (status.error_code < ARRAY_SIZE(scan_test_status)) {
dev_info(dev, "CPU(s) %*pbl: SCAN operation did not start. %s\n",
cpumask_pr_args(cpu_smt_mask(cpu)),
......@@ -90,16 +103,6 @@ static void message_fail(struct device *dev, int cpu, union ifs_status status)
{
struct ifs_data *ifsd = ifs_get_data(dev);
/*
* control_error is set when the microcode runs into a problem
* loading the image from the reserved BIOS memory, or it has
* been corrupted. Reloading the image may fix this issue.
*/
if (status.control_error) {
dev_err(dev, "CPU(s) %*pbl: could not execute from loaded scan image. Batch: %02x version: 0x%x\n",
cpumask_pr_args(cpu_smt_mask(cpu)), ifsd->cur_batch, ifsd->loaded_version);
}
/*
* signature_error is set when the output from the scan chains does not
* match the expected signature. This might be a transient problem (e.g.
......@@ -285,10 +288,10 @@ static void ifs_test_core(int cpu, struct device *dev)
/* Update status for this core */
ifsd->scan_details = status.data;
if (status.control_error || status.signature_error) {
if (status.signature_error) {
ifsd->status = SCAN_TEST_FAIL;
message_fail(dev, cpu, status);
} else if (status.error_code) {
} else if (status.control_error || status.error_code) {
ifsd->status = SCAN_NOT_TESTED;
message_not_tested(dev, cpu, status);
} else {
......
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains platform specific structure definitions
* and init function used by Meteor Lake PCH.
* and init function used by Arrow Lake PCH.
*
* Copyright (c) 2022, Intel Corporation.
* All Rights Reserved.
......
......@@ -678,6 +678,41 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr);
static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
unsigned int pmcidx;
for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) {
const struct pmc_bit_map **maps;
unsigned int arr_size, r_idx;
u32 offset, counter;
struct pmc *pmc;
pmc = pmcdev->pmcs[pmcidx];
if (!pmc)
continue;
maps = pmc->map->s0ix_blocker_maps;
offset = pmc->map->s0ix_blocker_offset;
arr_size = pmc_core_lpm_get_arr_size(maps);
for (r_idx = 0; r_idx < arr_size; r_idx++) {
const struct pmc_bit_map *map;
for (map = maps[r_idx]; map->name; map++) {
if (!map->blk)
continue;
counter = pmc_core_reg_read(pmc, offset);
seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx,
map->name, counter);
offset += map->blk * S0IX_BLK_SIZE;
}
}
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker);
static inline u64 adjust_lpm_residency(struct pmc *pmc, u32 offset,
const int lpm_adj_x2)
{
......@@ -1197,6 +1232,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
debugfs_create_file("ltr_show", 0444, dir, pmcdev, &pmc_core_ltr_fops);
if (primary_pmc->map->s0ix_blocker_maps)
debugfs_create_file("s0ix_blocker", 0444, dir, pmcdev, &pmc_core_s0ix_blocker_fops);
debugfs_create_file("package_cstate_show", 0444, dir, primary_pmc,
&pmc_core_pkgc_fops);
......
......@@ -22,6 +22,7 @@ struct telem_endpoint;
#define PMC_BASE_ADDR_DEFAULT 0xFE000000
#define MAX_NUM_PMC 3
#define S0IX_BLK_SIZE 4
/* Sunrise Point Power Management Controller PCI Device ID */
#define SPT_PMC_PCI_DEVICE_ID 0x9d21
......@@ -282,12 +283,14 @@ enum ppfear_regs {
#define LNL_PMC_LTR_OSSE 0x1B88
#define LNL_NUM_IP_IGN_ALLOWED 27
#define LNL_PPFEAR_NUM_ENTRIES 12
#define LNL_S0IX_BLOCKER_OFFSET 0x2004
extern const char *pmc_lpm_modes[];
struct pmc_bit_map {
const char *name;
u32 bit_mask;
u8 blk;
};
/**
......@@ -298,6 +301,7 @@ struct pmc_bit_map {
* @pll_sts: Maps name of PLL to corresponding bit status
* @slps0_dbg_maps: Array of SLP_S0_DBG* registers containing debug info
* @ltr_show_sts: Maps PCH IP Names to their MMIO register offsets
* @s0ix_blocker_maps: Maps name of IP block to S0ix blocker counter
* @slp_s0_offset: PWRMBASE offset to read SLP_S0 residency
* @ltr_ignore_offset: PWRMBASE offset to read/write LTR ignore bit
* @regmap_length: Length of memory to map from PWRMBASE address to access
......@@ -307,6 +311,7 @@ struct pmc_bit_map {
* @pm_cfg_offset: PWRMBASE offset to PM_CFG register
* @pm_read_disable_bit: Bit index to read PMC_READ_DISABLE
* @slps0_dbg_offset: PWRMBASE offset to SLP_S0_DEBUG_REG*
* @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter
*
* Each PCH has unique set of register offsets and bit indexes. This structure
* captures them to have a common implementation.
......@@ -319,6 +324,7 @@ struct pmc_reg_map {
const struct pmc_bit_map *ltr_show_sts;
const struct pmc_bit_map *msr_sts;
const struct pmc_bit_map **lpm_sts;
const struct pmc_bit_map **s0ix_blocker_maps;
const u32 slp_s0_offset;
const int slp_s0_res_counter_step;
const u32 ltr_ignore_offset;
......@@ -330,6 +336,7 @@ struct pmc_reg_map {
const u32 slps0_dbg_offset;
const u32 ltr_ignore_max;
const u32 pm_vric1_offset;
const u32 s0ix_blocker_offset;
/* Low Power Mode registers */
const int lpm_num_maps;
const int lpm_num_modes;
......@@ -535,8 +542,10 @@ extern const struct pmc_bit_map lnl_vnn_req_status_2_map[];
extern const struct pmc_bit_map lnl_vnn_req_status_3_map[];
extern const struct pmc_bit_map lnl_vnn_misc_status_map[];
extern const struct pmc_bit_map *lnl_lpm_maps[];
extern const struct pmc_bit_map *lnl_blk_maps[];
extern const struct pmc_bit_map lnl_pfear_map[];
extern const struct pmc_bit_map *ext_lnl_pfear_map[];
extern const struct pmc_bit_map lnl_signal_status_map[];
/* ARL */
extern const struct pmc_bit_map arl_socs_ltr_show_map[];
......
This diff is collapsed.
......@@ -15,6 +15,7 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
......@@ -66,6 +67,8 @@
#define CTRL_OWNER GENMASK(5, 4)
#define CTRL_COMPLETE BIT(6)
#define CTRL_READY BIT(7)
#define CTRL_INBAND_LOCK BIT(32)
#define CTRL_METER_ENABLE_DRAM BIT(33)
#define CTRL_STATUS GENMASK(15, 8)
#define CTRL_PACKET_SIZE GENMASK(31, 16)
#define CTRL_MSG_SIZE GENMASK(63, 48)
......@@ -93,6 +96,7 @@ enum sdsi_command {
struct sdsi_mbox_info {
u64 *payload;
void *buffer;
u64 control_flags;
int size;
};
......@@ -156,8 +160,8 @@ static int sdsi_status_to_errno(u32 status)
}
}
static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
size_t *data_size)
static int sdsi_mbox_poll(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
size_t *data_size)
{
struct device *dev = priv->dev;
u32 total, loop, eom, status, message_size;
......@@ -166,18 +170,10 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
lockdep_assert_held(&priv->mb_lock);
/* Format and send the read command */
control = FIELD_PREP(CTRL_EOM, 1) |
FIELD_PREP(CTRL_SOM, 1) |
FIELD_PREP(CTRL_RUN_BUSY, 1) |
FIELD_PREP(CTRL_PACKET_SIZE, info->size);
writeq(control, priv->control_addr);
/* For reads, data sizes that are larger than the mailbox size are read in packets. */
total = 0;
loop = 0;
do {
void *buf = info->buffer + (SDSI_SIZE_MAILBOX * loop);
u32 packet_size;
/* Poll on ready bit */
......@@ -195,6 +191,11 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
if (ret)
break;
if (!packet_size) {
sdsi_complete_transaction(priv);
break;
}
/* Only the last packet can be less than the mailbox size. */
if (!eom && packet_size != SDSI_SIZE_MAILBOX) {
dev_err(dev, "Invalid packet size\n");
......@@ -208,9 +209,13 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
break;
}
sdsi_memcpy64_fromio(buf, priv->mbox_addr, round_up(packet_size, SDSI_SIZE_CMD));
if (info->buffer) {
void *buf = info->buffer + array_size(SDSI_SIZE_MAILBOX, loop);
total += packet_size;
sdsi_memcpy64_fromio(buf, priv->mbox_addr,
round_up(packet_size, SDSI_SIZE_CMD));
total += packet_size;
}
sdsi_complete_transaction(priv);
} while (!eom && ++loop < MBOX_MAX_PACKETS);
......@@ -230,16 +235,34 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
dev_warn(dev, "Read count %u differs from expected count %u\n",
total, message_size);
*data_size = total;
if (data_size)
*data_size = total;
return 0;
}
static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
size_t *data_size)
{
u64 control;
lockdep_assert_held(&priv->mb_lock);
/* Format and send the read command */
control = FIELD_PREP(CTRL_EOM, 1) |
FIELD_PREP(CTRL_SOM, 1) |
FIELD_PREP(CTRL_RUN_BUSY, 1) |
FIELD_PREP(CTRL_PACKET_SIZE, info->size) |
info->control_flags;
writeq(control, priv->control_addr);
return sdsi_mbox_poll(priv, info, data_size);
}
static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
size_t *data_size)
{
u64 control;
u32 status;
int ret;
lockdep_assert_held(&priv->mb_lock);
......@@ -252,23 +275,11 @@ static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *in
FIELD_PREP(CTRL_SOM, 1) |
FIELD_PREP(CTRL_RUN_BUSY, 1) |
FIELD_PREP(CTRL_READ_WRITE, 1) |
FIELD_PREP(CTRL_MSG_SIZE, info->size) |
FIELD_PREP(CTRL_PACKET_SIZE, info->size);
writeq(control, priv->control_addr);
/* Poll on ready bit */
ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY,
MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
if (ret)
goto release_mbox;
status = FIELD_GET(CTRL_STATUS, control);
ret = sdsi_status_to_errno(status);
release_mbox:
sdsi_complete_transaction(priv);
return ret;
return sdsi_mbox_poll(priv, info, data_size);
}
static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
......@@ -312,7 +323,8 @@ static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info
return ret;
}
static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
size_t *data_size)
{
int ret;
......@@ -322,7 +334,7 @@ static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
if (ret)
return ret;
return sdsi_mbox_cmd_write(priv, info);
return sdsi_mbox_cmd_write(priv, info, data_size);
}
static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size)
......@@ -338,15 +350,24 @@ static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, s
return sdsi_mbox_cmd_read(priv, info, data_size);
}
static bool sdsi_ib_locked(struct sdsi_priv *priv)
{
return !!FIELD_GET(CTRL_INBAND_LOCK, readq(priv->control_addr));
}
static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
enum sdsi_command command)
{
struct sdsi_mbox_info info;
struct sdsi_mbox_info info = {};
int ret;
if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD))
return -EOVERFLOW;
/* Make sure In-band lock is not set */
if (sdsi_ib_locked(priv))
return -EPERM;
/* Qword aligned message + command qword */
info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD;
......@@ -363,7 +384,9 @@ static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
ret = mutex_lock_interruptible(&priv->mb_lock);
if (ret)
goto free_payload;
ret = sdsi_mbox_write(priv, &info);
ret = sdsi_mbox_write(priv, &info, NULL);
mutex_unlock(&priv->mb_lock);
free_payload:
......@@ -404,10 +427,10 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
static ssize_t
certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off,
size_t count)
certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv,
char *buf, loff_t off, size_t count)
{
struct sdsi_mbox_info info;
struct sdsi_mbox_info info = {};
size_t size;
int ret;
......@@ -421,6 +444,7 @@ certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off,
info.payload = &command;
info.size = sizeof(command);
info.control_flags = control_flags;
ret = mutex_lock_interruptible(&priv->mb_lock);
if (ret)
......@@ -452,7 +476,7 @@ state_certificate_read(struct file *filp, struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
return certificate_read(SDSI_CMD_READ_STATE, priv, buf, off, count);
return certificate_read(SDSI_CMD_READ_STATE, 0, priv, buf, off, count);
}
static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG);
......@@ -464,10 +488,23 @@ meter_certificate_read(struct file *filp, struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
return certificate_read(SDSI_CMD_READ_METER, priv, buf, off, count);
return certificate_read(SDSI_CMD_READ_METER, 0, priv, buf, off, count);
}
static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG);
static ssize_t
meter_current_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
return certificate_read(SDSI_CMD_READ_METER, CTRL_METER_ENABLE_DRAM,
priv, buf, off, count);
}
static BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG);
static ssize_t registers_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
......@@ -498,6 +535,7 @@ static struct bin_attribute *sdsi_bin_attrs[] = {
&bin_attr_registers,
&bin_attr_state_certificate,
&bin_attr_meter_certificate,
&bin_attr_meter_current,
&bin_attr_provision_akc,
&bin_attr_provision_cap,
NULL
......@@ -517,7 +555,7 @@ sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n)
if (!(priv->features & SDSI_FEATURE_SDSI))
return 0;
if (attr == &bin_attr_meter_certificate)
if (attr == &bin_attr_meter_certificate || attr == &bin_attr_meter_current)
return (priv->features & SDSI_FEATURE_METERING) ?
attr->attr.mode : 0;
......
......@@ -839,4 +839,5 @@ void isst_if_cdev_unregister(int device_type)
}
EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
MODULE_DESCRIPTION("ISST common interface module");
MODULE_LICENSE("GPL v2");
......@@ -128,6 +128,9 @@ struct intel_tpmi_info {
* @dev: PCI device number
* @bus: PCI bus number
* @pkg: CPU Package id
* @segment: PCI segment id
* @partition: Package Partition id
* @cdie_mask: Bitmap of compute dies in the current partition
* @reserved: Reserved for future use
* @lock: When set to 1 the register is locked and becomes read-only
* until next reset. Not for use by the OS driver.
......@@ -139,7 +142,10 @@ struct tpmi_info_header {
u64 dev:5;
u64 bus:8;
u64 pkg:8;
u64 reserved:39;
u64 segment:8;
u64 partition:2;
u64 cdie_mask:16;
u64 reserved:13;
u64 lock:1;
} __packed;
......@@ -666,28 +672,44 @@ static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info)
}
#define TPMI_INFO_BUS_INFO_OFFSET 0x08
#define TPMI_INFO_MAJOR_VERSION 0x00
#define TPMI_INFO_MINOR_VERSION 0x02
static int tpmi_process_info(struct intel_tpmi_info *tpmi_info,
struct intel_tpmi_pm_feature *pfs)
{
struct tpmi_info_header header;
void __iomem *info_mem;
u64 feature_header;
int ret = 0;
info_mem = ioremap(pfs->vsec_offset + TPMI_INFO_BUS_INFO_OFFSET,
pfs->pfs_header.entry_size * sizeof(u32) - TPMI_INFO_BUS_INFO_OFFSET);
info_mem = ioremap(pfs->vsec_offset, pfs->pfs_header.entry_size * sizeof(u32));
if (!info_mem)
return -ENOMEM;
memcpy_fromio(&header, info_mem, sizeof(header));
feature_header = readq(info_mem);
if (TPMI_MAJOR_VERSION(feature_header) != TPMI_INFO_MAJOR_VERSION) {
ret = -ENODEV;
goto error_info_header;
}
memcpy_fromio(&header, info_mem + TPMI_INFO_BUS_INFO_OFFSET, sizeof(header));
tpmi_info->plat_info.package_id = header.pkg;
tpmi_info->plat_info.bus_number = header.bus;
tpmi_info->plat_info.device_number = header.dev;
tpmi_info->plat_info.function_number = header.fn;
if (TPMI_MINOR_VERSION(feature_header) >= TPMI_INFO_MINOR_VERSION) {
tpmi_info->plat_info.cdie_mask = header.cdie_mask;
tpmi_info->plat_info.partition = header.partition;
tpmi_info->plat_info.segment = header.segment;
}
error_info_header:
iounmap(info_mem);
return 0;
return ret;
}
static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size)
......@@ -763,8 +785,11 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
* when actual device nodes created outside this
* loop via tpmi_create_devices().
*/
if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID)
tpmi_process_info(tpmi_info, pfs);
if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID) {
ret = tpmi_process_info(tpmi_info, pfs);
if (ret)
return ret;
}
if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
tpmi_set_control_base(auxdev, tpmi_info, pfs);
......
......@@ -240,6 +240,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
bool read_blocked = 0, write_blocked = 0;
struct intel_tpmi_plat_info *plat_info;
struct tpmi_uncore_struct *tpmi_uncore;
bool uncore_sysfs_added = false;
int ret, i, pkg = 0;
int num_resources;
......@@ -384,9 +385,15 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
}
/* Point to next cluster offset */
cluster_offset >>= UNCORE_MAX_CLUSTER_PER_DOMAIN;
uncore_sysfs_added = true;
}
}
if (!uncore_sysfs_added) {
ret = -ENODEV;
goto remove_clusters;
}
auxiliary_set_drvdata(auxdev, tpmi_uncore);
tpmi_uncore->root_cluster.root_domain = true;
......
......@@ -156,7 +156,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
if ((ke = sparse_keymap_entry_from_scancode(priv->buttons_dev, event))) {
if (!priv->has_buttons) {
dev_warn(&device->dev, "Warning: received a button event on a device without buttons, please report this.\n");
dev_warn(&device->dev, "Warning: received 0x%02x button event on a device without buttons, please report this.\n",
event);
return;
}
input_dev = priv->buttons_dev;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -317,7 +317,7 @@ static ssize_t show_wlan(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
return sysfs_emit(buf, "%i\n", enabled);
}
static ssize_t store_wlan(struct device *dev,
......@@ -341,7 +341,7 @@ static ssize_t show_bluetooth(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
return sysfs_emit(buf, "%i\n", enabled);
}
static ssize_t store_bluetooth(struct device *dev,
......@@ -364,7 +364,7 @@ static ssize_t show_threeg(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", threeg_s);
return sysfs_emit(buf, "%i\n", threeg_s);
}
static ssize_t store_threeg(struct device *dev,
......@@ -383,7 +383,7 @@ static ssize_t show_lcd_level(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
return sysfs_emit(buf, "%i\n", ret);
}
static ssize_t store_lcd_level(struct device *dev,
......@@ -413,7 +413,7 @@ static ssize_t show_auto_brightness(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
return sysfs_emit(buf, "%i\n", ret);
}
static ssize_t store_auto_brightness(struct device *dev,
......@@ -443,7 +443,7 @@ static ssize_t show_touchpad(struct device *dev,
if (result < 0)
return result;
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
}
static ssize_t show_turbo(struct device *dev,
......@@ -457,7 +457,7 @@ static ssize_t show_turbo(struct device *dev,
if (result < 0)
return result;
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
}
static ssize_t show_eco(struct device *dev,
......@@ -471,7 +471,7 @@ static ssize_t show_eco(struct device *dev,
if (result < 0)
return result;
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
}
static ssize_t show_turbo_cooldown(struct device *dev,
......@@ -485,7 +485,7 @@ static ssize_t show_turbo_cooldown(struct device *dev,
if (result < 0)
return result;
return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
return sysfs_emit(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
(!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
}
......@@ -500,7 +500,7 @@ static ssize_t show_auto_fan(struct device *dev,
if (result < 0)
return result;
return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
}
static ssize_t store_auto_fan(struct device *dev,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -661,9 +661,9 @@ static ssize_t get_performance_level(struct device *dev,
/* The logic is backwards, yeah, lots of fun... */
for (i = 0; config->performance_levels[i].name; ++i) {
if (sretval.data[0] == config->performance_levels[i].value)
return sprintf(buf, "%s\n", config->performance_levels[i].name);
return sysfs_emit(buf, "%s\n", config->performance_levels[i].name);
}
return sprintf(buf, "%s\n", "unknown");
return sysfs_emit(buf, "%s\n", "unknown");
}
static ssize_t set_performance_level(struct device *dev,
......@@ -744,7 +744,7 @@ static ssize_t get_battery_life_extender(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t set_battery_life_extender(struct device *dev,
......@@ -813,7 +813,7 @@ static ssize_t get_usb_charge(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t set_usb_charge(struct device *dev,
......@@ -878,7 +878,7 @@ static ssize_t get_lid_handling(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t set_lid_handling(struct device *dev,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment