Commit 9c3255a8 authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull x86 platform driver updates from Hans de Goede:
 "AMD PMC and PMF drivers:
   - Various bugfixes
   - Improved debugging support

  Intel PMC:
   - Refactor to support hw with multiple PMCs
   - Various other improvements / new hw support

  Intel Speed Select Technology (ISST):
   - TPMI Uncore Frequency + Cluster Level Power Controls
   - Various bugfixes
   - tools/intel-speed-select: Misc improvements

  Dell-DDV: Add documentation

  INT3472 ACPI camera sensor glue code:
   - Evaluate device's _DSM method to control imaging clock
   - Drop the need to have a table with per sensor-model info

  Lenovo Yogabook:
   - Refactor / rework to also support Android models

  Think-LMI:
   - Multiple improvements and fixes

  WMI:
   - Add proper API documentation for the WMI bus

  x86-android-tablets:
   - Misc new hw support

  Miscellaneous other cleanups / fixes"

* tag 'platform-drivers-x86-v6.5-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (91 commits)
  platform/x86:intel/pmc: Add Meteor Lake IOE-M PMC related maps
  platform/x86:intel/pmc: Add Meteor Lake IOE-P PMC related maps
  platform/x86:intel/pmc: Use SSRAM to discover pwrm base address of primary PMC
  platform/x86:intel/pmc: Discover PMC devices
  platform/x86:intel/pmc: Enable debugfs multiple PMC support
  platform/x86:intel/pmc: Add support to handle multiple PMCs
  platform/x86:intel/pmc: Combine core_init() and core_configure()
  platform/x86:intel/pmc: Update maps for Meteor Lake P/M platforms
  platform/x86/intel: tpmi: Remove hardcoded unit and offset
  platform/x86: int3472: discrete: Log a warning if the pin-numbers don't match
  platform/x86: int3472: discrete: Use FIELD_GET() on the GPIO _DSM return value
  platform/x86: int3472: discrete: Add alternative "AVDD" regulator supply name
  platform/x86: int3472: discrete: Add support for 1 GPIO regulator shared between 2 sensors
  platform/x86: int3472: discrete: Remove sensor_config-s
  platform/x86: int3472: discrete: Drop GPIO remapping support
  platform/x86: apple-gmux: don't use be32_to_cpu and cpu_to_be32
  platform/x86/dell/dell-rbtn: Fix resources leaking on error path
  platform/x86: ISST: Fix usage counter
  platform/x86: ISST: Reset default callback on unregister
  platform/x86: int3472: Switch back to use struct i2c_driver's .probe()
  ...
parents 6cdbb090 d2a7bd36
What: /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof
Date: Jun 2017
KernelVersion: 4.13
Description:
Binary MOF metadata used to decribe the details of available ACPI WMI interfaces.
See Documentation/wmi/devices/wmi-bmof.rst for details.
......@@ -3,19 +3,32 @@ Date: September 2022
KernelVersion: 6.1
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
This file contains the contents of the fan sensor information buffer,
which contains fan sensor entries and a terminating character (0xFF).
This file contains the contents of the fan sensor information
buffer, which contains fan sensor entries and a terminating
character (0xFF).
Each fan sensor entry consists of three bytes with an unknown meaning,
interested people may use this file for reverse-engineering.
Each fan sensor entry contains:
- fan type (single byte)
- fan speed in RPM (two bytes, little endian)
See Documentation/wmi/devices/dell-wmi-ddv.rst for details.
What: /sys/kernel/debug/dell-wmi-ddv-<wmi_device_name>/thermal_sensor_information
Date: September 2022
KernelVersion: 6.1
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
This file contains the contents of the thermal sensor information buffer,
which contains thermal sensor entries and a terminating character (0xFF).
This file contains the contents of the thermal sensor information
buffer, which contains thermal sensor entries and a terminating
character (0xFF).
Each thermal sensor entry contains:
- thermal type (single byte)
- current temperature (single byte)
- min. temperature (single byte)
- max. temperature (single byte)
- unknown field (single byte)
Each thermal sensor entry consists of five bytes with an unknown meaning,
interested people may use this file for reverse-engineering.
See Documentation/wmi/devices/dell-wmi-ddv.rst for details.
......@@ -243,8 +243,8 @@ Description:
index:
Used with HDD and NVME authentication to set the drive index
that is being referenced (e.g hdd0, hdd1 etc)
This attribute defaults to device 0.
that is being referenced (e.g hdd1, hdd2 etc)
This attribute defaults to device 1.
certificate, signature, save_signature:
These attributes are used for certificate based authentication. This is
......
......@@ -3,5 +3,7 @@ Date: September 2022
KernelVersion: 6.1
Contact: Armin Wolf <W_Armin@gmx.de>
Description:
Reports the Dell ePPID (electronic Dell Piece Part Identification)
Reports the Dell ePPID (electronic Piece Part Identification)
of the ACPI battery.
See Documentation/wmi/devices/dell-wmi-ddv.rst for details.
......@@ -75,3 +75,12 @@ KernelVersion: 6.4
Contact: "Liming Sun <limings@nvidia.com>"
Description:
The file used to access the BlueField boot fifo.
What: /sys/bus/platform/devices/MLNXBF04:00/rsh_log
Date: May 2023
KernelVersion: 6.4
Contact: "Liming Sun <limings@nvidia.com>"
Description:
The file used to write BlueField boot log with the format
"[INFO|WARN|ERR|ASSERT ]<msg>". Log level 'INFO' is used by
default if not specified.
......@@ -5,7 +5,7 @@
Intel Uncore Frequency Scaling
==============================
:Copyright: |copy| 2022 Intel Corporation
:Copyright: |copy| 2022-2023 Intel Corporation
:Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
......@@ -58,3 +58,58 @@ Each package_*_die_* contains the following attributes:
``current_freq_khz``
This attribute is used to get the current uncore frequency.
SoCs with TPMI (Topology Aware Register and PM Capsule Interface)
-----------------------------------------------------------------
An SoC can contain multiple power domains with individual or collection
of mesh partitions. This partition is called fabric cluster.
Certain type of meshes will need to run at the same frequency, they will
be placed in the same fabric cluster. Benefit of fabric cluster is that it
offers a scalable mechanism to deal with partitioned fabrics in a SoC.
The current sysfs interface supports controls at package and die level.
This interface is not enough to support more granular control at
fabric cluster level.
SoCs with the support of TPMI (Topology Aware Register and PM Capsule
Interface), can have multiple power domains. Each power domain can
contain one or more fabric clusters.
To represent controls at fabric cluster level in addition to the
controls at package and die level (like systems without TPMI
support), sysfs is enhanced. This granular interface is presented in the
sysfs with directories names prefixed with "uncore". For example:
uncore00, uncore01 etc.
The scope of control is specified by attributes "package_id", "domain_id"
and "fabric_cluster_id" in the directory.
Attributes in each directory:
``domain_id``
This attribute is used to get the power domain id of this instance.
``fabric_cluster_id``
This attribute is used to get the fabric cluster id of this instance.
``package_id``
This attribute is used to get the package id of this instance.
The other attributes are same as presented at package_*_die_* level.
In most of current use cases, the "max_freq_khz" and "min_freq_khz"
is updated at "package_*_die_*" level. This model will be still supported
with the following approach:
When user uses controls at "package_*_die_*" level, then every fabric
cluster is affected in that package and die. For example: user changes
"max_freq_khz" in the package_00_die_00, then "max_freq_khz" for uncore*
directory with the same package id will be updated. In this case user can
still update "max_freq_khz" at each uncore* level, which is more restrictive.
Similarly, user can update "min_freq_khz" at "package_*_die_*" level
to apply at each uncore* level.
Support for "current_freq_khz" is available only at each fabric cluster
level (i.e., in uncore* directory).
......@@ -113,6 +113,7 @@ available subsections can be seen below.
xillybus
zorro
hte/index
wmi
.. only:: subproject and html
......
.. SPDX-License-Identifier: GPL-2.0-or-later
==============
WMI Driver API
==============
The WMI driver core supports a more modern bus-based interface for interacting
with WMI devices, and an older GUID-based interface. The latter interface is
considered to be deprecated, so new WMI drivers should generally avoid it since
it has some issues with multiple WMI devices and events sharing the same GUIDs
and/or notification IDs. The modern bus-based interface instead maps each
WMI device to a :c:type:`struct wmi_device <wmi_device>`, so it supports
WMI devices sharing GUIDs and/or notification IDs. Drivers can then register
a :c:type:`struct wmi_driver <wmi_driver>`, which will be bound to compatible
WMI devices by the driver core.
.. kernel-doc:: include/linux/wmi.h
:internal:
.. kernel-doc:: drivers/platform/x86/wmi.c
:export:
......@@ -71,3 +71,4 @@ Storage interfaces
scheduler/index
mhi/index
peci/index
wmi/index
.. SPDX-License-Identifier: GPL-2.0-or-later
==================
ACPI WMI interface
==================
The ACPI WMI interface is a proprietary extension of the ACPI specification made
by Microsoft to allow hardware vendors to embed WMI (Windows Management Instrumentation)
objects inside their ACPI firmware. Typical functions implemented over ACPI WMI
are hotkey events on modern notebooks and configuration of BIOS options.
PNP0C14 ACPI device
-------------------
Discovery of WMI objects is handled by defining ACPI devices with a PNP ID
of ``PNP0C14``. These devices will contain a set of ACPI buffers and methods
used for mapping and execution of WMI methods and/or queries. If there exist
multiple of such devices, then each device is required to have a
unique ACPI UID.
_WDG buffer
-----------
The ``_WDG`` buffer is used to discover WMI objects and is required to be
static. Its internal structure consists of data blocks with a size of 20 bytes,
containing the following data:
======= =============== =====================================================
Offset Size (in bytes) Content
======= =============== =====================================================
0x00 16 128 bit Variant 2 object GUID.
0x10 2 2 character method ID or single byte notification ID.
0x12 1 Object instance count.
0x13 1 Object flags.
======= =============== =====================================================
The WMI object flags control whether the method or notification ID is used:
- 0x1: Data block usage is expensive and must be explicitly enabled/disabled.
- 0x2: Data block contains WMI methods.
- 0x4: Data block contains ASCIZ string.
- 0x8: Data block describes a WMI event, use notification ID instead
of method ID.
Each WMI object GUID can appear multiple times inside a system.
The method/notification ID is used to construct the ACPI method names used for
interacting with the WMI object.
WQxx ACPI methods
-----------------
If a data block does not contain WMI methods, then its content can be retrieved
by this required ACPI method. The last two characters of the ACPI method name
are the method ID of the data block to query. Their single parameter is an
integer describing the instance which should be queried. This parameter can be
omitted if the data block contains only a single instance.
WSxx ACPI methods
-----------------
Similar to the ``WQxx`` ACPI methods, except that it is optional and takes an
additional buffer as its second argument. The instance argument also cannot
be omitted.
WMxx ACPI methods
-----------------
Used for executing WMI methods associated with a data block. The last two
characters of the ACPI method name are the method ID of the data block
containing the WMI methods. Their first parameter is a integer describing the
instance which methods should be executed. The second parameter is an integer
describing the WMI method ID to execute, and the third parameter is a buffer
containing the WMI method parameters. If the data block is marked as containing
an ASCIZ string, then this buffer should contain an ASCIZ string. The ACPI
method will return the result of the executed WMI method.
WExx ACPI methods
-----------------
Used for optionally enabling/disabling WMI events, the last two characters of
the ACPI method are the notification ID of the data block describing the WMI
event as hexadecimal value. Their first parameter is an integer with a value
of 0 if the WMI event should be disabled, other values will enable
the WMI event.
WCxx ACPI methods
-----------------
Similar to the ``WExx`` ACPI methods, except that it controls data collection
instead of events and thus the last two characters of the ACPI method name are
the method ID of the data block to enable/disable.
_WED ACPI method
----------------
Used to retrieve additional WMI event data, its single parameter is a integer
holding the notification ID of the event.
This diff is collapsed.
.. SPDX-License-Identifier: GPL-2.0-or-later
=============================
Driver-specific Documentation
=============================
This section provides information about various devices supported by
the Linux kernel, their protocols and driver details.
.. toctree::
:maxdepth: 1
:numbered:
:glob:
*
.. only:: subproject and html
Indices
=======
* :ref:`genindex`
.. SPDX-License-Identifier: GPL-2.0-only
==============================
WMI embedded Binary MOF driver
==============================
Introduction
============
Many machines embed WMI Binary MOF (Managed Object Format) metadata used to
describe the details of their ACPI WMI interfaces. The data can be decoded
with tools like `bmfdec <https://github.com/pali/bmfdec>`_ to obtain a
human readable WMI interface description, which is useful for developing
new WMI drivers.
The Binary MOF data can be retrieved from the ``bmof`` sysfs attribute of the
associated WMI device. Please note that multiple WMI devices containing Binary
MOF data can exist on a given system.
WMI interface
=============
The Binary MOF WMI device is identified by the WMI GUID ``05901221-D566-11D1-B2F0-00A0C9062910``.
The Binary MOF can be obtained by doing a WMI data block query. The result is
then returned as an ACPI buffer with a variable size.
.. SPDX-License-Identifier: GPL-2.0-or-later
=============
WMI Subsystem
=============
.. toctree::
:maxdepth: 1
acpi-interface
devices/index
.. only:: subproject and html
Indices
=======
* :ref:`genindex`
......@@ -456,6 +456,8 @@ F: include/linux/acpi_viot.h
ACPI WMI DRIVER
L: platform-driver-x86@vger.kernel.org
S: Orphan
F: Documentation/driver-api/wmi.rst
F: Documentation/wmi/
F: drivers/platform/x86/wmi.c
F: include/uapi/linux/wmi.h
......@@ -5842,6 +5844,7 @@ M: Armin Wolf <W_Armin@gmx.de>
S: Maintained
F: Documentation/ABI/testing/debugfs-dell-wmi-ddv
F: Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv
F: Documentation/wmi/devices/dell-wmi-ddv.rst
F: drivers/platform/x86/dell/dell-wmi-ddv.c
DELL WMI DESCRIPTOR DRIVER
......@@ -22910,6 +22913,13 @@ L: linux-wireless@vger.kernel.org
S: Odd fixes
F: drivers/net/wireless/legacy/wl3501*
WMI BINARY MOF DRIVER
L: platform-drivers-x86@vger.kernel.org
S: Orphan
F: Documentation/ABI/stable/sysfs-platform-wmi-bmof
F: Documentation/wmi/devices/wmi-bmof.rst
F: drivers/platform/x86/wmi-bmof.c
WOLFSON MICROELECTRONICS DRIVERS
L: patches@opensource.cirrus.com
S: Supported
......
......@@ -11,6 +11,7 @@
#include <linux/acpi.h>
#include <linux/arm-smccc.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
......@@ -45,10 +46,39 @@ static const char * const mlxbf_bootctl_lifecycle_states[] = {
[3] = "RMA",
};
/* Log header format. */
#define MLXBF_RSH_LOG_TYPE_MASK GENMASK_ULL(59, 56)
#define MLXBF_RSH_LOG_LEN_MASK GENMASK_ULL(54, 48)
#define MLXBF_RSH_LOG_LEVEL_MASK GENMASK_ULL(7, 0)
/* Log module ID and type (only MSG type in Linux driver for now). */
#define MLXBF_RSH_LOG_TYPE_MSG 0x04ULL
/* Log ctl/data register offset. */
#define MLXBF_RSH_SCRATCH_BUF_CTL_OFF 0
#define MLXBF_RSH_SCRATCH_BUF_DATA_OFF 0x10
/* Log message levels. */
enum {
MLXBF_RSH_LOG_INFO,
MLXBF_RSH_LOG_WARN,
MLXBF_RSH_LOG_ERR,
MLXBF_RSH_LOG_ASSERT
};
/* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */
static void __iomem *mlxbf_rsh_boot_data;
static void __iomem *mlxbf_rsh_boot_cnt;
/* Mapped pointer for rsh log semaphore/ctrl/data register. */
static void __iomem *mlxbf_rsh_semaphore;
static void __iomem *mlxbf_rsh_scratch_buf_ctl;
static void __iomem *mlxbf_rsh_scratch_buf_data;
/* Rsh log levels. */
static const char * const mlxbf_rsh_log_level[] = {
"INFO", "WARN", "ERR", "ASSERT"};
/* ARM SMC call which is atomic and no need for lock. */
static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
{
......@@ -266,12 +296,108 @@ static ssize_t fw_reset_store(struct device *dev,
return count;
}
/* Size(8-byte words) of the log buffer. */
#define RSH_SCRATCH_BUF_CTL_IDX_MASK 0x7f
/* 100ms timeout */
#define RSH_SCRATCH_BUF_POLL_TIMEOUT 100000
static int mlxbf_rsh_log_sem_lock(void)
{
unsigned long reg;
return readq_poll_timeout(mlxbf_rsh_semaphore, reg, !reg, 0,
RSH_SCRATCH_BUF_POLL_TIMEOUT);
}
static void mlxbf_rsh_log_sem_unlock(void)
{
writeq(0, mlxbf_rsh_semaphore);
}
static ssize_t rsh_log_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int rc, idx, num, len, level = MLXBF_RSH_LOG_INFO;
size_t size = count;
u64 data;
if (!size)
return -EINVAL;
if (!mlxbf_rsh_semaphore || !mlxbf_rsh_scratch_buf_ctl)
return -EOPNOTSUPP;
/* Ignore line break at the end. */
if (buf[size - 1] == '\n')
size--;
/* Check the message prefix. */
for (idx = 0; idx < ARRAY_SIZE(mlxbf_rsh_log_level); idx++) {
len = strlen(mlxbf_rsh_log_level[idx]);
if (len + 1 < size &&
!strncmp(buf, mlxbf_rsh_log_level[idx], len)) {
buf += len;
size -= len;
level = idx;
break;
}
}
/* Ignore leading spaces. */
while (size > 0 && buf[0] == ' ') {
size--;
buf++;
}
/* Take the semaphore. */
rc = mlxbf_rsh_log_sem_lock();
if (rc)
return rc;
/* Calculate how many words are available. */
idx = readq(mlxbf_rsh_scratch_buf_ctl);
num = min((int)DIV_ROUND_UP(size, sizeof(u64)),
RSH_SCRATCH_BUF_CTL_IDX_MASK - idx - 1);
if (num <= 0)
goto done;
/* Write Header. */
data = FIELD_PREP(MLXBF_RSH_LOG_TYPE_MASK, MLXBF_RSH_LOG_TYPE_MSG);
data |= FIELD_PREP(MLXBF_RSH_LOG_LEN_MASK, num);
data |= FIELD_PREP(MLXBF_RSH_LOG_LEVEL_MASK, level);
writeq(data, mlxbf_rsh_scratch_buf_data);
/* Write message. */
for (idx = 0; idx < num && size > 0; idx++) {
if (size < sizeof(u64)) {
data = 0;
memcpy(&data, buf, size);
size = 0;
} else {
memcpy(&data, buf, sizeof(u64));
size -= sizeof(u64);
buf += sizeof(u64);
}
writeq(data, mlxbf_rsh_scratch_buf_data);
}
done:
/* Release the semaphore. */
mlxbf_rsh_log_sem_unlock();
/* Ignore the rest if no more space. */
return count;
}
static DEVICE_ATTR_RW(post_reset_wdog);
static DEVICE_ATTR_RW(reset_action);
static DEVICE_ATTR_RW(second_reset_action);
static DEVICE_ATTR_RO(lifecycle_state);
static DEVICE_ATTR_RO(secure_boot_fuse_state);
static DEVICE_ATTR_WO(fw_reset);
static DEVICE_ATTR_WO(rsh_log);
static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_post_reset_wdog.attr,
......@@ -280,6 +406,7 @@ static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_lifecycle_state.attr,
&dev_attr_secure_boot_fuse_state.attr,
&dev_attr_fw_reset.attr,
&dev_attr_rsh_log.attr,
NULL
};
......@@ -345,19 +472,32 @@ static bool mlxbf_bootctl_guid_match(const guid_t *guid,
static int mlxbf_bootctl_probe(struct platform_device *pdev)
{
struct arm_smccc_res res = { 0 };
void __iomem *reg;
guid_t guid;
int ret;
/* Get the resource of the bootfifo data register. */
/* Map the resource of the bootfifo data register. */
mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mlxbf_rsh_boot_data))
return PTR_ERR(mlxbf_rsh_boot_data);
/* Get the resource of the bootfifo counter register. */
/* Map the resource of the bootfifo counter register. */
mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(mlxbf_rsh_boot_cnt))
return PTR_ERR(mlxbf_rsh_boot_cnt);
/* Map the resource of the rshim semaphore register. */
mlxbf_rsh_semaphore = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(mlxbf_rsh_semaphore))
return PTR_ERR(mlxbf_rsh_semaphore);
/* Map the resource of the scratch buffer (log) registers. */
reg = devm_platform_ioremap_resource(pdev, 3);
if (IS_ERR(reg))
return PTR_ERR(reg);
mlxbf_rsh_scratch_buf_ctl = reg + MLXBF_RSH_SCRATCH_BUF_CTL_OFF;
mlxbf_rsh_scratch_buf_data = reg + MLXBF_RSH_SCRATCH_BUF_DATA_OFF;
/* Ensure we have the UUID we expect for this service. */
arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
......
......@@ -573,7 +573,7 @@ static const struct acpi_device_id mshw0011_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, mshw0011_acpi_match);
static struct i2c_driver mshw0011_driver = {
.probe_new = mshw0011_probe,
.probe = mshw0011_probe,
.remove = mshw0011_remove,
.driver = {
.name = "mshw0011",
......
......@@ -43,8 +43,8 @@ config WMI_BMOF
default ACPI_WMI
help
Say Y here if you want to be able to read a firmware-embedded
WMI Binary MOF data. Using this requires userspace tools and may be
rather tedious.
WMI Binary MOF (Managed Object Format) data. Using this requires
userspace tools and may be rather tedious.
To compile this driver as a module, choose M here: the module will
be called wmi-bmof.
......@@ -121,10 +121,11 @@ config GIGABYTE_WMI
To compile this driver as a module, choose M here: the module will
be called gigabyte-wmi.
config YOGABOOK_WMI
tristate "Lenovo Yoga Book tablet WMI key driver"
config YOGABOOK
tristate "Lenovo Yoga Book tablet key driver"
depends on ACPI_WMI
depends on INPUT
depends on I2C
select LEDS_CLASS
select NEW_LEDS
help
......@@ -132,7 +133,7 @@ config YOGABOOK_WMI
control on the Lenovo Yoga Book tablets.
To compile this driver as a module, choose M here: the module will
be called lenovo-yogabook-wmi.
be called lenovo-yogabook.
config ACERHDF
tristate "Acer Aspire One temperature and fan driver"
......
......@@ -14,7 +14,6 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o
# Acer
obj-$(CONFIG_ACERHDF) += acerhdf.o
......@@ -66,6 +65,7 @@ obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o
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
# Intel
obj-y += intel/
......
......@@ -45,7 +45,6 @@
#define AMD_PMC_STB_DUMMY_PC 0xC6000007
/* STB S2D(Spill to DRAM) has different message port offset */
#define STB_SPILL_TO_DRAM 0xBE
#define AMD_S2D_REGISTER_MESSAGE 0xA20
#define AMD_S2D_REGISTER_RESPONSE 0xA80
#define AMD_S2D_REGISTER_ARGUMENT 0xA88
......@@ -99,7 +98,6 @@
#define PMC_MSG_DELAY_MIN_US 50
#define RESPONSE_REGISTER_LOOP_MAX 20000
#define SOC_SUBSYSTEM_IP_MAX 12
#define DELAY_MIN_US 2000
#define DELAY_MAX_US 3000
#define FIFO_SIZE 4096
......@@ -115,6 +113,7 @@ enum s2d_arg {
S2D_PHYS_ADDR_LOW,
S2D_PHYS_ADDR_HIGH,
S2D_NUM_SAMPLES,
S2D_DRAM_SIZE,
};
struct amd_pmc_bit_map {
......@@ -132,9 +131,18 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = {
{"ISP", BIT(6)},
{"NBIO", BIT(7)},
{"DF", BIT(8)},
{"USB0", BIT(9)},
{"USB1", BIT(10)},
{"USB3_0", BIT(9)},
{"USB3_1", BIT(10)},
{"LAPIC", BIT(11)},
{"USB3_2", BIT(12)},
{"USB3_3", BIT(13)},
{"USB3_4", BIT(14)},
{"USB4_0", BIT(15)},
{"USB4_1", BIT(16)},
{"MPM", BIT(17)},
{"JPEG", BIT(18)},
{"IPU", BIT(19)},
{"UMSCH", BIT(20)},
{}
};
......@@ -147,6 +155,9 @@ struct amd_pmc_dev {
u32 base_addr;
u32 cpu_id;
u32 active_ips;
u32 dram_size;
u32 num_ips;
u32 s2d_msg_id;
/* SMU version information */
u8 smu_program;
u8 major;
......@@ -194,8 +205,8 @@ struct smu_metrics {
u64 timein_s0i3_totaltime;
u64 timein_swdrips_lastcapture;
u64 timein_swdrips_totaltime;
u64 timecondition_notmet_lastcapture[SOC_SUBSYSTEM_IP_MAX];
u64 timecondition_notmet_totaltime[SOC_SUBSYSTEM_IP_MAX];
u64 timecondition_notmet_lastcapture[32];
u64 timecondition_notmet_totaltime[32];
} __packed;
static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp)
......@@ -261,7 +272,7 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
dev->msg_port = 1;
/* Get the num_samples to calculate the last push location */
ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, STB_SPILL_TO_DRAM, 1);
ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true);
/* Clear msg_port for other SMU operation */
dev->msg_port = 0;
if (ret) {
......@@ -308,6 +319,23 @@ static const struct file_operations amd_pmc_stb_debugfs_fops_v2 = {
.release = amd_pmc_stb_debugfs_release_v2,
};
static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
{
switch (dev->cpu_id) {
case AMD_CPU_ID_PCO:
case AMD_CPU_ID_RN:
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
dev->num_ips = 12;
dev->s2d_msg_id = 0xBE;
break;
case AMD_CPU_ID_PS:
dev->num_ips = 21;
dev->s2d_msg_id = 0x85;
break;
}
}
static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
{
if (dev->cpu_id == AMD_CPU_ID_PCO) {
......@@ -317,15 +345,15 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
/* Get Active devices list from SMU */
if (!dev->active_ips)
amd_pmc_send_cmd(dev, 0, &dev->active_ips, SMU_MSG_GET_SUP_CONSTRAINTS, 1);
amd_pmc_send_cmd(dev, 0, &dev->active_ips, SMU_MSG_GET_SUP_CONSTRAINTS, true);
/* Get dram address */
if (!dev->smu_virt_addr) {
u32 phys_addr_low, phys_addr_hi;
u64 smu_phys_addr;
amd_pmc_send_cmd(dev, 0, &phys_addr_low, SMU_MSG_LOG_GETDRAM_ADDR_LO, 1);
amd_pmc_send_cmd(dev, 0, &phys_addr_hi, SMU_MSG_LOG_GETDRAM_ADDR_HI, 1);
amd_pmc_send_cmd(dev, 0, &phys_addr_low, SMU_MSG_LOG_GETDRAM_ADDR_LO, true);
amd_pmc_send_cmd(dev, 0, &phys_addr_hi, SMU_MSG_LOG_GETDRAM_ADDR_HI, true);
smu_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
dev->smu_virt_addr = devm_ioremap(dev->dev, smu_phys_addr,
......@@ -335,8 +363,8 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
}
/* Start the logging */
amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_RESET, 0);
amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_START, 0);
amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_RESET, false);
amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_START, false);
return 0;
}
......@@ -377,7 +405,7 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
if (dev->cpu_id == AMD_CPU_ID_PCO)
return -ENODEV;
rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1);
rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, true);
if (rc)
return rc;
......@@ -469,7 +497,7 @@ static int smu_fw_info_show(struct seq_file *s, void *unused)
table.timeto_resume_to_os_lastcapture);
seq_puts(s, "\n=== Active time (in us) ===\n");
for (idx = 0 ; idx < SOC_SUBSYSTEM_IP_MAX ; idx++) {
for (idx = 0 ; idx < dev->num_ips ; idx++) {
if (soc15_ip_blk[idx].bit_mask & dev->active_ips)
seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name,
table.timecondition_notmet_lastcapture[idx]);
......@@ -562,6 +590,18 @@ static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
debugfs_remove_recursive(dev->dbgfs_dir);
}
static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev)
{
switch (dev->cpu_id) {
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
case AMD_CPU_ID_PS:
return true;
default:
return false;
}
}
static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
{
dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL);
......@@ -573,8 +613,7 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
&amd_pmc_idlemask_fops);
/* Enable STB only when the module_param is set */
if (enable_stb) {
if (dev->cpu_id == AMD_CPU_ID_YC || dev->cpu_id == AMD_CPU_ID_CB ||
dev->cpu_id == AMD_CPU_ID_PS)
if (amd_pmc_is_stb_supported(dev))
debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
&amd_pmc_stb_debugfs_fops_v2);
else
......@@ -794,7 +833,7 @@ static void amd_pmc_s2idle_prepare(void)
}
msg = amd_pmc_get_os_hint(pdev);
rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, 0);
rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false);
if (rc) {
dev_err(pdev->dev, "suspend failed: %d\n", rc);
return;
......@@ -829,7 +868,7 @@ static int amd_pmc_dump_data(struct amd_pmc_dev *pdev)
if (pdev->cpu_id == AMD_CPU_ID_PCO)
return -ENODEV;
return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0);
return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, false);
}
static void amd_pmc_s2idle_restore(void)
......@@ -839,7 +878,7 @@ static void amd_pmc_s2idle_restore(void)
u8 msg;
msg = amd_pmc_get_os_hint(pdev);
rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, 0);
rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false);
if (rc)
dev_err(pdev->dev, "resume failed: %d\n", rc);
......@@ -890,29 +929,65 @@ static const struct pci_device_id pmc_pci_ids[] = {
{ }
};
static int amd_pmc_get_dram_size(struct amd_pmc_dev *dev)
{
int ret;
switch (dev->cpu_id) {
case AMD_CPU_ID_YC:
if (!(dev->major > 90 || (dev->major == 90 && dev->minor > 39))) {
ret = -EINVAL;
goto err_dram_size;
}
break;
default:
ret = -EINVAL;
goto err_dram_size;
}
ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true);
if (ret || !dev->dram_size)
goto err_dram_size;
return 0;
err_dram_size:
dev_err(dev->dev, "DRAM size command not supported for this platform\n");
return ret;
}
static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)
{
u32 phys_addr_low, phys_addr_hi;
u64 stb_phys_addr;
u32 size = 0;
int ret;
/* Spill to DRAM feature uses separate SMU message port */
dev->msg_port = 1;
amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, STB_SPILL_TO_DRAM, 1);
/* Get num of IP blocks within the SoC */
amd_pmc_get_ip_info(dev);
amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true);
if (size != S2D_TELEMETRY_BYTES_MAX)
return -EIO;
/* Get DRAM size */
ret = amd_pmc_get_dram_size(dev);
if (ret)
dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
/* Get STB DRAM address */
amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, STB_SPILL_TO_DRAM, 1);
amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, STB_SPILL_TO_DRAM, 1);
amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true);
amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true);
stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
/* Clear msg_port for other SMU operation */
dev->msg_port = 0;
dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, S2D_TELEMETRY_DRAMBYTES_MAX);
dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
if (!dev->stb_virt_addr)
return -ENOMEM;
......@@ -1001,7 +1076,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
mutex_init(&dev->lock);
if (enable_stb && (dev->cpu_id == AMD_CPU_ID_YC || dev->cpu_id == AMD_CPU_ID_CB)) {
if (enable_stb && amd_pmc_is_stb_supported(dev)) {
err = amd_pmc_s2d_init(dev);
if (err)
goto err_pci_dev_put;
......
......@@ -16,3 +16,14 @@ config AMD_PMF
To compile this driver as a module, choose M here: the module will
be called amd_pmf.
config AMD_PMF_DEBUG
bool "PMF debug information"
depends on AMD_PMF
help
Enabling this option would give more debug information on the OEM fed
power setting values for each of the PMF feature. PMF driver gets this
information after evaluating a ACPI method and the information is stored
in the PMF config store.
Say Y here to enable more debug logs and Say N here if you are not sure.
......@@ -15,6 +15,100 @@
static struct auto_mode_mode_config config_store;
static const char *state_as_str(unsigned int state);
#ifdef CONFIG_AMD_PMF_DEBUG
static void amd_pmf_dump_auto_mode_defaults(struct auto_mode_mode_config *data)
{
struct auto_mode_mode_settings *its_mode;
pr_debug("Auto Mode Data - BEGIN\n");
/* time constant */
pr_debug("balanced_to_perf: %u ms\n",
data->transition[AUTO_TRANSITION_TO_PERFORMANCE].time_constant);
pr_debug("perf_to_balanced: %u ms\n",
data->transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].time_constant);
pr_debug("quiet_to_balanced: %u ms\n",
data->transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].time_constant);
pr_debug("balanced_to_quiet: %u ms\n",
data->transition[AUTO_TRANSITION_TO_QUIET].time_constant);
/* power floor */
pr_debug("pfloor_perf: %u mW\n", data->mode_set[AUTO_PERFORMANCE].power_floor);
pr_debug("pfloor_balanced: %u mW\n", data->mode_set[AUTO_BALANCE].power_floor);
pr_debug("pfloor_quiet: %u mW\n", data->mode_set[AUTO_QUIET].power_floor);
/* Power delta for mode change */
pr_debug("pd_balanced_to_perf: %u mW\n",
data->transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta);
pr_debug("pd_perf_to_balanced: %u mW\n",
data->transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta);
pr_debug("pd_quiet_to_balanced: %u mW\n",
data->transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta);
pr_debug("pd_balanced_to_quiet: %u mW\n",
data->transition[AUTO_TRANSITION_TO_QUIET].power_delta);
/* skin temperature limits */
its_mode = &data->mode_set[AUTO_PERFORMANCE_ON_LAP];
pr_debug("stt_apu_perf_on_lap: %u C\n",
its_mode->power_control.stt_skin_temp[STT_TEMP_APU]);
pr_debug("stt_hs2_perf_on_lap: %u C\n",
its_mode->power_control.stt_skin_temp[STT_TEMP_HS2]);
pr_debug("stt_min_limit_perf_on_lap: %u mW\n", its_mode->power_control.stt_min);
its_mode = &data->mode_set[AUTO_PERFORMANCE];
pr_debug("stt_apu_perf: %u C\n", its_mode->power_control.stt_skin_temp[STT_TEMP_APU]);
pr_debug("stt_hs2_perf: %u C\n", its_mode->power_control.stt_skin_temp[STT_TEMP_HS2]);
pr_debug("stt_min_limit_perf: %u mW\n", its_mode->power_control.stt_min);
its_mode = &data->mode_set[AUTO_BALANCE];
pr_debug("stt_apu_balanced: %u C\n", its_mode->power_control.stt_skin_temp[STT_TEMP_APU]);
pr_debug("stt_hs2_balanced: %u C\n", its_mode->power_control.stt_skin_temp[STT_TEMP_HS2]);
pr_debug("stt_min_limit_balanced: %u mW\n", its_mode->power_control.stt_min);
its_mode = &data->mode_set[AUTO_QUIET];
pr_debug("stt_apu_quiet: %u C\n", its_mode->power_control.stt_skin_temp[STT_TEMP_APU]);
pr_debug("stt_hs2_quiet: %u C\n", its_mode->power_control.stt_skin_temp[STT_TEMP_HS2]);
pr_debug("stt_min_limit_quiet: %u mW\n", its_mode->power_control.stt_min);
/* SPL based power limits */
its_mode = &data->mode_set[AUTO_PERFORMANCE_ON_LAP];
pr_debug("fppt_perf_on_lap: %u mW\n", its_mode->power_control.fppt);
pr_debug("sppt_perf_on_lap: %u mW\n", its_mode->power_control.sppt);
pr_debug("spl_perf_on_lap: %u mW\n", its_mode->power_control.spl);
pr_debug("sppt_apu_only_perf_on_lap: %u mW\n", its_mode->power_control.sppt_apu_only);
its_mode = &data->mode_set[AUTO_PERFORMANCE];
pr_debug("fppt_perf: %u mW\n", its_mode->power_control.fppt);
pr_debug("sppt_perf: %u mW\n", its_mode->power_control.sppt);
pr_debug("spl_perf: %u mW\n", its_mode->power_control.spl);
pr_debug("sppt_apu_only_perf: %u mW\n", its_mode->power_control.sppt_apu_only);
its_mode = &data->mode_set[AUTO_BALANCE];
pr_debug("fppt_balanced: %u mW\n", its_mode->power_control.fppt);
pr_debug("sppt_balanced: %u mW\n", its_mode->power_control.sppt);
pr_debug("spl_balanced: %u mW\n", its_mode->power_control.spl);
pr_debug("sppt_apu_only_balanced: %u mW\n", its_mode->power_control.sppt_apu_only);
its_mode = &data->mode_set[AUTO_QUIET];
pr_debug("fppt_quiet: %u mW\n", its_mode->power_control.fppt);
pr_debug("sppt_quiet: %u mW\n", its_mode->power_control.sppt);
pr_debug("spl_quiet: %u mW\n", its_mode->power_control.spl);
pr_debug("sppt_apu_only_quiet: %u mW\n", its_mode->power_control.sppt_apu_only);
/* Fan ID */
pr_debug("fan_id_perf: %lu\n",
data->mode_set[AUTO_PERFORMANCE].fan_control.fan_id);
pr_debug("fan_id_balanced: %lu\n",
data->mode_set[AUTO_BALANCE].fan_control.fan_id);
pr_debug("fan_id_quiet: %lu\n",
data->mode_set[AUTO_QUIET].fan_control.fan_id);
pr_debug("Auto Mode Data - END\n");
}
#else
static void amd_pmf_dump_auto_mode_defaults(struct auto_mode_mode_config *data) {}
#endif
static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx,
struct auto_mode_mode_config *table)
{
......@@ -85,11 +179,34 @@ void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t t
config_store.transition[i].applied = false;
update = true;
}
#ifdef CONFIG_AMD_PMF_DEBUG
dev_dbg(dev->dev, "[AUTO MODE] average_power : %d mW mode: %s\n", avg_power,
state_as_str(config_store.current_mode));
dev_dbg(dev->dev, "[AUTO MODE] time: %lld ms timer: %u ms tc: %u ms\n",
time_elapsed_ms, config_store.transition[i].timer,
config_store.transition[i].time_constant);
dev_dbg(dev->dev, "[AUTO MODE] shiftup: %u pt: %u mW pf: %u mW pd: %u mW\n",
config_store.transition[i].shifting_up,
config_store.transition[i].power_threshold,
config_store.mode_set[i].power_floor,
config_store.transition[i].power_delta);
#endif
}
dev_dbg(dev->dev, "[AUTO_MODE] avg power: %u mW mode: %s\n", avg_power,
state_as_str(config_store.current_mode));
#ifdef CONFIG_AMD_PMF_DEBUG
dev_dbg(dev->dev, "[AUTO MODE] priority1: %u priority2: %u priority3: %u priority4: %u\n",
config_store.transition[0].applied,
config_store.transition[1].applied,
config_store.transition[2].applied,
config_store.transition[3].applied);
#endif
if (update) {
for (j = 0; j < AUTO_TRANSITION_MAX; j++) {
/* Apply the mode with highest priority indentified */
......@@ -140,6 +257,30 @@ static void amd_pmf_get_power_threshold(void)
config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_threshold =
config_store.mode_set[AUTO_PERFORMANCE].power_floor -
config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta;
#ifdef CONFIG_AMD_PMF_DEBUG
pr_debug("[AUTO MODE TO_QUIET] pt: %u mW pf: %u mW pd: %u mW\n",
config_store.transition[AUTO_TRANSITION_TO_QUIET].power_threshold,
config_store.mode_set[AUTO_BALANCE].power_floor,
config_store.transition[AUTO_TRANSITION_TO_QUIET].power_delta);
pr_debug("[AUTO MODE TO_PERFORMANCE] pt: %u mW pf: %u mW pd: %u mW\n",
config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_threshold,
config_store.mode_set[AUTO_BALANCE].power_floor,
config_store.transition[AUTO_TRANSITION_TO_PERFORMANCE].power_delta);
pr_debug("[AUTO MODE QUIET_TO_BALANCE] pt: %u mW pf: %u mW pd: %u mW\n",
config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE]
.power_threshold,
config_store.mode_set[AUTO_QUIET].power_floor,
config_store.transition[AUTO_TRANSITION_FROM_QUIET_TO_BALANCE].power_delta);
pr_debug("[AUTO MODE PERFORMANCE_TO_BALANCE] pt: %u mW pf: %u mW pd: %u mW\n",
config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE]
.power_threshold,
config_store.mode_set[AUTO_PERFORMANCE].power_floor,
config_store.transition[AUTO_TRANSITION_FROM_PERFORMANCE_TO_BALANCE].power_delta);
#endif
}
static const char *state_as_str(unsigned int state)
......@@ -262,6 +403,8 @@ static void amd_pmf_load_defaults_auto_mode(struct amd_pmf_dev *dev)
/* set to initial default values */
config_store.current_mode = AUTO_BALANCE;
dev->socket_power_history_idx = -1;
amd_pmf_dump_auto_mode_defaults(&config_store);
}
int amd_pmf_reset_amt(struct amd_pmf_dev *dev)
......
......@@ -13,6 +13,61 @@
static struct cnqf_config config_store;
#ifdef CONFIG_AMD_PMF_DEBUG
static const char *state_as_str_cnqf(unsigned int state)
{
switch (state) {
case APMF_CNQF_TURBO:
return "turbo";
case APMF_CNQF_PERFORMANCE:
return "performance";
case APMF_CNQF_BALANCE:
return "balance";
case APMF_CNQF_QUIET:
return "quiet";
default:
return "Unknown CnQF State";
}
}
static void amd_pmf_cnqf_dump_defaults(struct apmf_dyn_slider_output *data, int idx)
{
int i;
pr_debug("Dynamic Slider %s Defaults - BEGIN\n", idx ? "DC" : "AC");
pr_debug("size: %u\n", data->size);
pr_debug("flags: 0x%x\n", data->flags);
/* Time constants */
pr_debug("t_perf_to_turbo: %u ms\n", data->t_perf_to_turbo);
pr_debug("t_balanced_to_perf: %u ms\n", data->t_balanced_to_perf);
pr_debug("t_quiet_to_balanced: %u ms\n", data->t_quiet_to_balanced);
pr_debug("t_balanced_to_quiet: %u ms\n", data->t_balanced_to_quiet);
pr_debug("t_perf_to_balanced: %u ms\n", data->t_perf_to_balanced);
pr_debug("t_turbo_to_perf: %u ms\n", data->t_turbo_to_perf);
for (i = 0 ; i < CNQF_MODE_MAX ; i++) {
pr_debug("pfloor_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].pfloor);
pr_debug("fppt_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].fppt);
pr_debug("sppt_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].sppt);
pr_debug("sppt_apuonly_%s: %u mW\n",
state_as_str_cnqf(i), data->ps[i].sppt_apu_only);
pr_debug("spl_%s: %u mW\n", state_as_str_cnqf(i), data->ps[i].spl);
pr_debug("stt_minlimit_%s: %u mW\n",
state_as_str_cnqf(i), data->ps[i].stt_min_limit);
pr_debug("stt_skintemp_apu_%s: %u C\n", state_as_str_cnqf(i),
data->ps[i].stt_skintemp[STT_TEMP_APU]);
pr_debug("stt_skintemp_hs2_%s: %u C\n", state_as_str_cnqf(i),
data->ps[i].stt_skintemp[STT_TEMP_HS2]);
pr_debug("fan_id_%s: %u\n", state_as_str_cnqf(i), data->ps[i].fan_id);
}
pr_debug("Dynamic Slider %s Defaults - END\n", idx ? "DC" : "AC");
}
#else
static void amd_pmf_cnqf_dump_defaults(struct apmf_dyn_slider_output *data, int idx) {}
#endif
static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx,
struct cnqf_config *table)
{
......@@ -120,6 +175,13 @@ int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_l
config_store.trans_param[src][i].count++;
tp = &config_store.trans_param[src][i];
#ifdef CONFIG_AMD_PMF_DEBUG
dev_dbg(dev->dev, "avg_power: %u mW total_power: %u mW count: %u timer: %u ms\n",
avg_power, config_store.trans_param[src][i].total_power,
config_store.trans_param[src][i].count,
config_store.trans_param[src][i].timer);
#endif
if (tp->timer >= tp->time_constant && tp->count) {
avg_power = tp->total_power / tp->count;
......@@ -140,6 +202,18 @@ int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_l
dev_dbg(dev->dev, "[CNQF] Avg power: %u mW socket power: %u mW mode:%s\n",
avg_power, socket_power, state_as_str(config_store.current_mode));
#ifdef CONFIG_AMD_PMF_DEBUG
dev_dbg(dev->dev, "[CNQF] priority1: %u priority2: %u priority3: %u\n",
config_store.trans_param[src][0].priority,
config_store.trans_param[src][1].priority,
config_store.trans_param[src][2].priority);
dev_dbg(dev->dev, "[CNQF] priority4: %u priority5: %u priority6: %u\n",
config_store.trans_param[src][3].priority,
config_store.trans_param[src][4].priority,
config_store.trans_param[src][5].priority);
#endif
for (j = 0; j < CNQF_TRANSITION_MAX; j++) {
/* apply the highest priority */
if (config_store.trans_param[src][j].priority) {
......@@ -284,6 +358,7 @@ static int amd_pmf_load_defaults_cnqf(struct amd_pmf_dev *dev)
return ret;
}
amd_pmf_cnqf_dump_defaults(&out, i);
amd_pmf_update_mode_set(i, &out);
amd_pmf_update_trans_data(i, &out);
amd_pmf_update_power_threshold(i);
......
......@@ -12,6 +12,60 @@
static struct amd_pmf_static_slider_granular config_store;
#ifdef CONFIG_AMD_PMF_DEBUG
static const char *slider_as_str(unsigned int state)
{
switch (state) {
case POWER_MODE_PERFORMANCE:
return "PERFORMANCE";
case POWER_MODE_BALANCED_POWER:
return "BALANCED_POWER";
case POWER_MODE_POWER_SAVER:
return "POWER_SAVER";
default:
return "Unknown Slider State";
}
}
static const char *source_as_str(unsigned int state)
{
switch (state) {
case POWER_SOURCE_AC:
return "AC";
case POWER_SOURCE_DC:
return "DC";
default:
return "Unknown Power State";
}
}
static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data)
{
int i, j;
pr_debug("Static Slider Data - BEGIN\n");
for (i = 0; i < POWER_SOURCE_MAX; i++) {
for (j = 0; j < POWER_MODE_MAX; j++) {
pr_debug("--- Source:%s Mode:%s ---\n", source_as_str(i), slider_as_str(j));
pr_debug("SPL: %u mW\n", data->prop[i][j].spl);
pr_debug("SPPT: %u mW\n", data->prop[i][j].sppt);
pr_debug("SPPT_ApuOnly: %u mW\n", data->prop[i][j].sppt_apu_only);
pr_debug("FPPT: %u mW\n", data->prop[i][j].fppt);
pr_debug("STTMinLimit: %u mW\n", data->prop[i][j].stt_min);
pr_debug("STT_SkinTempLimit_APU: %u C\n",
data->prop[i][j].stt_skin_temp[STT_TEMP_APU]);
pr_debug("STT_SkinTempLimit_HS2: %u C\n",
data->prop[i][j].stt_skin_temp[STT_TEMP_HS2]);
}
}
pr_debug("Static Slider Data - END\n");
}
#else
static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data) {}
#endif
static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
{
struct apmf_static_slider_granular_output output;
......@@ -36,6 +90,7 @@ static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
idx++;
}
}
amd_pmf_dump_sps_defaults(&config_store);
}
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
......
......@@ -278,7 +278,7 @@ static u32 gmux_mmio_read32(struct apple_gmux_data *gmux_data, int port)
iowrite8(GMUX_MMIO_READ | sizeof(val),
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
gmux_mmio_wait(gmux_data);
val = be32_to_cpu(ioread32(gmux_data->iomem_base));
val = ioread32be(gmux_data->iomem_base);
mutex_unlock(&gmux_data->index_lock);
return val;
......@@ -288,7 +288,7 @@ static void gmux_mmio_write32(struct apple_gmux_data *gmux_data, int port,
u32 val)
{
mutex_lock(&gmux_data->index_lock);
iowrite32(cpu_to_be32(val), gmux_data->iomem_base);
iowrite32be(val, gmux_data->iomem_base);
iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT);
iowrite8(GMUX_MMIO_WRITE | sizeof(val),
gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND);
......
......@@ -933,7 +933,7 @@ static struct i2c_driver tf103c_dock_driver = {
.pm = &tf103c_dock_pm_ops,
.acpi_match_table = tf103c_dock_acpi_match,
},
.probe_new = tf103c_dock_probe,
.probe = tf103c_dock_probe,
.remove = tf103c_dock_remove,
};
module_i2c_driver(tf103c_dock_driver);
......
......@@ -395,16 +395,16 @@ static int rbtn_add(struct acpi_device *device)
return -EINVAL;
}
rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
if (!rbtn_data)
return -ENOMEM;
ret = rbtn_acquire(device, true);
if (ret < 0) {
dev_err(&device->dev, "Cannot enable device\n");
return ret;
}
rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
if (!rbtn_data)
return -ENOMEM;
rbtn_data->type = type;
device->driver_data = rbtn_data;
......@@ -420,10 +420,12 @@ static int rbtn_add(struct acpi_device *device)
break;
default:
ret = -EINVAL;
break;
}
if (ret)
rbtn_acquire(device, false);
return ret;
}
static void rbtn_remove(struct acpi_device *device)
......@@ -442,7 +444,6 @@ static void rbtn_remove(struct acpi_device *device)
}
rbtn_acquire(device, false);
device->driver_data = NULL;
}
static void rbtn_notify(struct acpi_device *device, u32 event)
......
......@@ -303,16 +303,13 @@ union acpi_object *get_wmiobj_pointer(int instance_id, const char *guid_string)
*/
int get_instance_count(const char *guid_string)
{
union acpi_object *wmi_obj = NULL;
int i = 0;
int ret;
do {
kfree(wmi_obj);
wmi_obj = get_wmiobj_pointer(i, guid_string);
i++;
} while (wmi_obj);
ret = wmi_instance_count(guid_string);
if (ret < 0)
return 0;
return (i-1);
return ret;
}
/**
......
......@@ -5,7 +5,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/wmi.h>
......@@ -13,10 +12,6 @@
#define GIGABYTE_WMI_GUID "DEADBEEF-2001-0000-00A0-C90629100000"
#define NUM_TEMPERATURE_SENSORS 6
static bool force_load;
module_param(force_load, bool, 0444);
MODULE_PARM_DESC(force_load, "Force loading on unknown platform");
static u8 usable_sensors_mask;
enum gigabyte_wmi_commandtype {
......@@ -99,7 +94,7 @@ static umode_t gigabyte_wmi_hwmon_is_visible(const void *data, enum hwmon_sensor
return usable_sensors_mask & BIT(channel) ? 0444 : 0;
}
static const struct hwmon_channel_info *gigabyte_wmi_hwmon_info[] = {
static const struct hwmon_channel_info * const gigabyte_wmi_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT,
HWMON_T_INPUT,
......@@ -133,49 +128,10 @@ static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev)
return r;
}
#define DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME(name) \
{ .matches = { \
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), \
DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
}}
static const struct dmi_system_id gigabyte_wmi_known_working_platforms[] = {
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("A320M-S2H V2-CF"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M DS3H-CF"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M DS3H WIFI-CF"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M S2H V2"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE AX V2"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE V2"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 GAMING X V2"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550I AORUS PRO AX"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M AORUS PRO-P"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M DS3H"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B650 AORUS ELITE AX"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B660 GAMING X DDR4"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B660I AORUS PRO DDR4"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z390 I AORUS PRO WIFI-CF"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z490 AORUS ELITE AC"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 AORUS ELITE"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 AORUS ELITE WIFI"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 GAMING X"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 I AORUS PRO WIFI"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570 UD"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("X570S AORUS ELITE"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z690M AORUS ELITE AX DDR4"),
{ }
};
static int gigabyte_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct device *hwmon_dev;
if (!dmi_check_system(gigabyte_wmi_known_working_platforms)) {
if (!force_load)
return -ENODEV;
dev_warn(&wdev->dev, "Forcing load on unknown platform");
}
usable_sensors_mask = gigabyte_wmi_detect_sensor_usability(wdev);
if (!usable_sensors_mask) {
dev_info(&wdev->dev, "No temperature sensors usable");
......
......@@ -66,6 +66,11 @@ static const char *const omen_thermal_profile_force_v0_boards[] = {
"8607", "8746", "8747", "8749", "874A", "8748"
};
/* DMI Board names of Victus laptops */
static const char * const victus_thermal_profile_boards[] = {
"8A25"
};
enum hp_wmi_radio {
HPWMI_WIFI = 0x0,
HPWMI_BLUETOOTH = 0x1,
......@@ -90,6 +95,7 @@ enum hp_wmi_event_ids {
HPWMI_PEAKSHIFT_PERIOD = 0x0F,
HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
HPWMI_SANITIZATION_MODE = 0x17,
HPWMI_CAMERA_TOGGLE = 0x1A,
HPWMI_OMEN_KEY = 0x1D,
HPWMI_SMART_EXPERIENCE_APP = 0x21,
};
......@@ -176,6 +182,12 @@ enum hp_thermal_profile_omen_v1 {
HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
};
enum hp_thermal_profile_victus {
HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00,
HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01,
HP_VICTUS_THERMAL_PROFILE_QUIET = 0x03,
};
enum hp_thermal_profile {
HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
HP_THERMAL_PROFILE_DEFAULT = 0x01,
......@@ -222,6 +234,7 @@ static const struct key_entry hp_wmi_keymap[] = {
{ KE_IGNORE, 0x121a4, }, /* Win Lock Off */
{ KE_KEY, 0x21a5, { KEY_PROG2 } }, /* HP Omen Key */
{ KE_KEY, 0x21a7, { KEY_FN_ESC } },
{ KE_KEY, 0x21a8, { KEY_PROG2 } }, /* HP Envy x360 programmable key */
{ KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } },
{ KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } },
{ KE_KEY, 0x231b, { KEY_HELP } },
......@@ -229,6 +242,7 @@ static const struct key_entry hp_wmi_keymap[] = {
};
static struct input_dev *hp_wmi_input_dev;
static struct input_dev *camera_shutter_input_dev;
static struct platform_device *hp_wmi_platform_dev;
static struct platform_profile_handler platform_profile_handler;
static bool platform_profile_support;
......@@ -740,6 +754,33 @@ static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
return count;
}
static int camera_shutter_input_setup(void)
{
int err;
camera_shutter_input_dev = input_allocate_device();
if (!camera_shutter_input_dev)
return -ENOMEM;
camera_shutter_input_dev->name = "HP WMI camera shutter";
camera_shutter_input_dev->phys = "wmi/input1";
camera_shutter_input_dev->id.bustype = BUS_HOST;
__set_bit(EV_SW, camera_shutter_input_dev->evbit);
__set_bit(SW_CAMERA_LENS_COVER, camera_shutter_input_dev->swbit);
err = input_register_device(camera_shutter_input_dev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
input_free_device(camera_shutter_input_dev);
camera_shutter_input_dev = NULL;
return err;
}
static DEVICE_ATTR_RO(display);
static DEVICE_ATTR_RO(hddtemp);
static DEVICE_ATTR_RW(als);
......@@ -816,11 +857,20 @@ static void hp_wmi_notify(u32 value, void *context)
case HPWMI_SMART_ADAPTER:
break;
case HPWMI_BEZEL_BUTTON:
case HPWMI_OMEN_KEY:
key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY);
if (key_code < 0)
break;
if (!sparse_keymap_report_event(hp_wmi_input_dev,
key_code, 1, true))
pr_info("Unknown key code - 0x%x\n", key_code);
break;
case HPWMI_OMEN_KEY:
if (event_data) /* Only should be true for HP Omen */
key_code = event_data;
else
key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY);
if (!sparse_keymap_report_event(hp_wmi_input_dev,
key_code, 1, true))
pr_info("Unknown key code - 0x%x\n", key_code);
......@@ -867,6 +917,20 @@ static void hp_wmi_notify(u32 value, void *context)
break;
case HPWMI_SANITIZATION_MODE:
break;
case HPWMI_CAMERA_TOGGLE:
if (!camera_shutter_input_dev)
if (camera_shutter_input_setup()) {
pr_err("Failed to setup camera shutter input device\n");
break;
}
if (event_data == 0xff)
input_report_switch(camera_shutter_input_dev, SW_CAMERA_LENS_COVER, 1);
else if (event_data == 0xfe)
input_report_switch(camera_shutter_input_dev, SW_CAMERA_LENS_COVER, 0);
else
pr_warn("Unknown camera shutter state - 0x%x\n", event_data);
input_sync(camera_shutter_input_dev);
break;
case HPWMI_SMART_EXPERIENCE_APP:
break;
default:
......@@ -1246,6 +1310,70 @@ static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof,
return 0;
}
static bool is_victus_thermal_profile(void)
{
const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
if (!board_name)
return false;
return match_string(victus_thermal_profile_boards,
ARRAY_SIZE(victus_thermal_profile_boards),
board_name) >= 0;
}
static int platform_profile_victus_get(struct platform_profile_handler *pprof,
enum platform_profile_option *profile)
{
int tp;
tp = omen_thermal_profile_get();
if (tp < 0)
return tp;
switch (tp) {
case HP_VICTUS_THERMAL_PROFILE_PERFORMANCE:
*profile = PLATFORM_PROFILE_PERFORMANCE;
break;
case HP_VICTUS_THERMAL_PROFILE_DEFAULT:
*profile = PLATFORM_PROFILE_BALANCED;
break;
case HP_VICTUS_THERMAL_PROFILE_QUIET:
*profile = PLATFORM_PROFILE_QUIET;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int platform_profile_victus_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
int err, tp;
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
tp = HP_VICTUS_THERMAL_PROFILE_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
tp = HP_VICTUS_THERMAL_PROFILE_DEFAULT;
break;
case PLATFORM_PROFILE_QUIET:
tp = HP_VICTUS_THERMAL_PROFILE_QUIET;
break;
default:
return -EOPNOTSUPP;
}
err = omen_thermal_profile_set(tp);
if (err < 0)
return err;
return 0;
}
static int thermal_profile_setup(void)
{
int err, tp;
......@@ -1266,6 +1394,25 @@ static int thermal_profile_setup(void)
platform_profile_handler.profile_get = platform_profile_omen_get;
platform_profile_handler.profile_set = platform_profile_omen_set;
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
} else if (is_victus_thermal_profile()) {
tp = omen_thermal_profile_get();
if (tp < 0)
return tp;
/*
* call thermal profile write command to ensure that the
* firmware correctly sets the OEM variables
*/
err = omen_thermal_profile_set(tp);
if (err < 0)
return err;
platform_profile_handler.profile_get = platform_profile_victus_get;
platform_profile_handler.profile_set = platform_profile_victus_set;
set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices);
} else {
tp = thermal_profile_get();
......@@ -1284,9 +1431,9 @@ static int thermal_profile_setup(void)
platform_profile_handler.profile_set = hp_wmi_platform_profile_set;
set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices);
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
}
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices);
set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices);
......@@ -1483,7 +1630,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
}
}
static const struct hwmon_channel_info *info[] = {
static const struct hwmon_channel_info * const info[] = {
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT),
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE),
NULL
......@@ -1565,6 +1712,9 @@ static void __exit hp_wmi_exit(void)
if (wmi_has_guid(HPWMI_EVENT_GUID))
hp_wmi_input_destroy();
if (camera_shutter_input_dev)
input_unregister_device(camera_shutter_input_dev);
if (hp_wmi_platform_dev) {
platform_device_unregister(hp_wmi_platform_dev);
platform_driver_unregister(&hp_wmi_driver);
......
......@@ -5,12 +5,48 @@
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/driver.h>
#include <linux/slab.h>
#include "common.h"
/*
* 82c0d13a-78c5-4244-9bb1-eb8b539a8d11
* This _DSM GUID allows controlling the sensor clk when it is not controlled
* through a GPIO.
*/
static const guid_t img_clk_guid =
GUID_INIT(0x82c0d13a, 0x78c5, 0x4244,
0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11);
static void skl_int3472_enable_clk(struct int3472_clock *clk, int enable)
{
struct int3472_discrete_device *int3472 = to_int3472_device(clk);
union acpi_object args[3];
union acpi_object argv4;
if (clk->ena_gpio) {
gpiod_set_value_cansleep(clk->ena_gpio, enable);
return;
}
args[0].integer.type = ACPI_TYPE_INTEGER;
args[0].integer.value = clk->imgclk_index;
args[1].integer.type = ACPI_TYPE_INTEGER;
args[1].integer.value = enable;
args[2].integer.type = ACPI_TYPE_INTEGER;
args[2].integer.value = 1;
argv4.type = ACPI_TYPE_PACKAGE;
argv4.package.count = 3;
argv4.package.elements = args;
acpi_evaluate_dsm(acpi_device_handle(int3472->adev), &img_clk_guid,
0, 1, &argv4);
}
/*
* The regulators have to have .ops to be valid, but the only ops we actually
* support are .enable and .disable which are handled via .ena_gpiod. Pass an
......@@ -20,17 +56,13 @@ static const struct regulator_ops int3472_gpio_regulator_ops;
static int skl_int3472_clk_prepare(struct clk_hw *hw)
{
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
gpiod_set_value_cansleep(clk->ena_gpio, 1);
skl_int3472_enable_clk(to_int3472_clk(hw), 1);
return 0;
}
static void skl_int3472_clk_unprepare(struct clk_hw *hw)
{
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
gpiod_set_value_cansleep(clk->ena_gpio, 0);
skl_int3472_enable_clk(to_int3472_clk(hw), 0);
}
static int skl_int3472_clk_enable(struct clk_hw *hw)
......@@ -73,7 +105,7 @@ static unsigned int skl_int3472_get_clk_frequency(struct int3472_discrete_device
static unsigned long skl_int3472_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
struct int3472_clock *clk = to_int3472_clk(hw);
return clk->frequency;
}
......@@ -86,7 +118,50 @@ static const struct clk_ops skl_int3472_clock_ops = {
.recalc_rate = skl_int3472_clk_recalc_rate,
};
int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472)
{
struct acpi_device *adev = int3472->adev;
struct clk_init_data init = {
.ops = &skl_int3472_clock_ops,
.flags = CLK_GET_RATE_NOCACHE,
};
int ret;
if (int3472->clock.cl)
return 0; /* A GPIO controlled clk has already been registered */
if (!acpi_check_dsm(adev->handle, &img_clk_guid, 0, BIT(1)))
return 0; /* DSM clock control is not available */
init.name = kasprintf(GFP_KERNEL, "%s-clk", acpi_dev_name(adev));
if (!init.name)
return -ENOMEM;
int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472);
int3472->clock.clk_hw.init = &init;
int3472->clock.clk = clk_register(&adev->dev, &int3472->clock.clk_hw);
if (IS_ERR(int3472->clock.clk)) {
ret = PTR_ERR(int3472->clock.clk);
goto out_free_init_name;
}
int3472->clock.cl = clkdev_create(int3472->clock.clk, NULL, int3472->sensor_name);
if (!int3472->clock.cl) {
ret = -ENOMEM;
goto err_unregister_clk;
}
kfree(init.name);
return 0;
err_unregister_clk:
clk_unregister(int3472->clock.clk);
out_free_init_name:
kfree(init.name);
return ret;
}
int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u32 polarity)
{
char *path = agpio->resource_source.string_ptr;
......@@ -160,32 +235,73 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)
gpiod_put(int3472->clock.ena_gpio);
}
/*
* The INT3472 device is going to be the only supplier of a regulator for
* the sensor device. But unlike the clk framework the regulator framework
* does not allow matching by consumer-device-name only.
*
* Ideally all sensor drivers would use "avdd" as supply-id. But for drivers
* where this cannot be changed because another supply-id is already used in
* e.g. DeviceTree files an alias for the other supply-id can be added here.
*
* Do not forget to update GPIO_REGULATOR_SUPPLY_MAP_COUNT when changing this.
*/
static const char * const skl_int3472_regulator_map_supplies[] = {
"avdd",
"AVDD",
};
static_assert(ARRAY_SIZE(skl_int3472_regulator_map_supplies) ==
GPIO_REGULATOR_SUPPLY_MAP_COUNT);
/*
* On some models there is a single GPIO regulator which is shared between
* sensors and only listed in the ACPI resources of one sensor.
* This DMI table contains the name of the second sensor. This is used to add
* entries for the second sensor to the supply_map.
*/
const struct dmi_system_id skl_int3472_regulator_second_sensor[] = {
{
/* Lenovo Miix 510-12IKB */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "MIIX 510-12IKB"),
},
.driver_data = "i2c-OVTI2680:00",
},
{ }
};
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio)
{
const struct int3472_sensor_config *sensor_config;
char *path = agpio->resource_source.string_ptr;
struct regulator_consumer_supply supply_map;
struct regulator_init_data init_data = { };
struct regulator_config cfg = { };
int ret;
sensor_config = int3472->sensor_config;
if (IS_ERR(sensor_config)) {
dev_err(int3472->dev, "No sensor module config\n");
return PTR_ERR(sensor_config);
const char *second_sensor = NULL;
const struct dmi_system_id *id;
int i, j, ret;
id = dmi_first_match(skl_int3472_regulator_second_sensor);
if (id)
second_sensor = id->driver_data;
for (i = 0, j = 0; i < ARRAY_SIZE(skl_int3472_regulator_map_supplies); i++) {
int3472->regulator.supply_map[j].supply = skl_int3472_regulator_map_supplies[i];
int3472->regulator.supply_map[j].dev_name = int3472->sensor_name;
j++;
if (second_sensor) {
int3472->regulator.supply_map[j].supply =
skl_int3472_regulator_map_supplies[i];
int3472->regulator.supply_map[j].dev_name = second_sensor;
j++;
}
if (!sensor_config->supply_map.supply) {
dev_err(int3472->dev, "No supply name defined\n");
return -ENODEV;
}
init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
init_data.num_consumer_supplies = 1;
supply_map = sensor_config->supply_map;
supply_map.dev_name = int3472->sensor_name;
init_data.consumer_supplies = &supply_map;
init_data.consumer_supplies = int3472->regulator.supply_map;
init_data.num_consumer_supplies = j;
snprintf(int3472->regulator.regulator_name,
sizeof(int3472->regulator.regulator_name), "%s-regulator",
......
......@@ -28,6 +28,7 @@
#define GPIO_REGULATOR_NAME_LENGTH 21
#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
#define GPIO_REGULATOR_SUPPLY_MAP_COUNT 2
#define INT3472_LED_MAX_NAME_LEN 32
......@@ -43,7 +44,7 @@
}
#define to_int3472_clk(hw) \
container_of(hw, struct int3472_gpio_clock, clk_hw)
container_of(hw, struct int3472_clock, clk_hw)
#define to_int3472_device(clk) \
container_of(clk, struct int3472_discrete_device, clock)
......@@ -64,18 +65,9 @@ struct int3472_cldb {
u8 control_logic_type;
u8 control_logic_id;
u8 sensor_card_sku;
u8 reserved[28];
};
struct int3472_gpio_function_remap {
const char *documented;
const char *actual;
};
struct int3472_sensor_config {
const char *sensor_module_name;
struct regulator_consumer_supply supply_map;
const struct int3472_gpio_function_remap *function_maps;
u8 reserved[10];
u8 clock_source;
u8 reserved2[17];
};
struct int3472_discrete_device {
......@@ -87,6 +79,8 @@ struct int3472_discrete_device {
const struct int3472_sensor_config *sensor_config;
struct int3472_gpio_regulator {
/* SUPPLY_MAP_COUNT * 2 to make room for second sensor mappings */
struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT * 2];
char regulator_name[GPIO_REGULATOR_NAME_LENGTH];
char supply_name[GPIO_REGULATOR_SUPPLY_NAME_LENGTH];
struct gpio_desc *gpio;
......@@ -94,12 +88,13 @@ struct int3472_discrete_device {
struct regulator_desc rdesc;
} regulator;
struct int3472_gpio_clock {
struct int3472_clock {
struct clk *clk;
struct clk_hw clk_hw;
struct clk_lookup *cl;
struct gpio_desc *ena_gpio;
u32 frequency;
u8 imgclk_index;
} clock;
struct int3472_pled {
......@@ -121,8 +116,9 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
struct acpi_device **sensor_adev_ret,
const char **name_ret);
int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u32 polarity);
int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472);
void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
......
......@@ -2,6 +2,7 @@
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
......@@ -25,6 +26,10 @@ static const guid_t int3472_gpio_guid =
GUID_INIT(0x79234640, 0x9e10, 0x4fea,
0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
#define INT3472_GPIO_DSM_TYPE GENMASK(7, 0)
#define INT3472_GPIO_DSM_PIN GENMASK(15, 8)
#define INT3472_GPIO_DSM_SENSOR_ON_VAL GENMASK(31, 24)
/*
* 822ace8f-2814-4174-a56b-5f029fe079ee
* This _DSM GUID returns a string from the sensor device, which acts as a
......@@ -34,69 +39,23 @@ static const guid_t cio2_sensor_module_guid =
GUID_INIT(0x822ace8f, 0x2814, 0x4174,
0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
/*
* Here follows platform specific mapping information that we can pass to
* the functions mapping resources to the sensors. Where the sensors have
* a power enable pin defined in DSDT we need to provide a supply name so
* the sensor drivers can find the regulator. The device name will be derived
* from the sensor's ACPI device within the code. Optionally, we can provide a
* NULL terminated array of function name mappings to deal with any platform
* specific deviations from the documented behaviour of GPIOs.
*
* Map a GPIO function name to NULL to prevent the driver from mapping that
* GPIO at all.
*/
static const struct int3472_gpio_function_remap ov2680_gpio_function_remaps[] = {
{ "reset", NULL },
{ "powerdown", "reset" },
{ }
};
static const struct int3472_sensor_config int3472_sensor_configs[] = {
/* Lenovo Miix 510-12ISK - OV2680, Front */
{ "GNDF140809R", { 0 }, ov2680_gpio_function_remaps },
/* Lenovo Miix 510-12ISK - OV5648, Rear */
{ "GEFF150023R", REGULATOR_SUPPLY("avdd", NULL), NULL },
/* Surface Go 1&2 - OV5693, Front */
{ "YHCU", REGULATOR_SUPPLY("avdd", NULL), NULL },
};
static const struct int3472_sensor_config *
skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472)
static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *int3472)
{
union acpi_object *obj;
unsigned int i;
obj = acpi_evaluate_dsm_typed(int3472->sensor->handle,
&cio2_sensor_module_guid, 0x00,
0x01, NULL, ACPI_TYPE_STRING);
if (!obj) {
dev_err(int3472->dev,
"Failed to get sensor module string from _DSM\n");
return ERR_PTR(-ENODEV);
}
for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) {
if (!strcmp(int3472_sensor_configs[i].sensor_module_name,
obj->string.pointer))
break;
}
if (obj) {
dev_dbg(int3472->dev, "Sensor module id: '%s'\n", obj->string.pointer);
ACPI_FREE(obj);
if (i >= ARRAY_SIZE(int3472_sensor_configs))
return ERR_PTR(-EINVAL);
return &int3472_sensor_configs[i];
}
}
static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio,
const char *func, u32 polarity)
{
const struct int3472_sensor_config *sensor_config;
char *path = agpio->resource_source.string_ptr;
struct gpiod_lookup *table_entry;
struct acpi_device *adev;
......@@ -108,22 +67,6 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
return -EINVAL;
}
sensor_config = int3472->sensor_config;
if (!IS_ERR(sensor_config) && sensor_config->function_maps) {
const struct int3472_gpio_function_remap *remap;
for (remap = sensor_config->function_maps; remap->documented; remap++) {
if (!strcmp(func, remap->documented)) {
func = remap->actual;
break;
}
}
}
/* Functions mapped to NULL should not be mapped to the sensor */
if (!func)
return 0;
status = acpi_get_handle(NULL, path, &handle);
if (ACPI_FAILURE(status))
return -EINVAL;
......@@ -211,8 +154,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
{
struct int3472_discrete_device *int3472 = data;
struct acpi_resource_gpio *agpio;
u8 active_value, pin, type;
union acpi_object *obj;
u8 active_value, type;
const char *err_msg;
const char *func;
u32 polarity;
......@@ -236,12 +179,17 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
return 1;
}
type = obj->integer.value & 0xff;
type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);
int3472_get_func_and_polarity(type, &func, &polarity);
/* If bits 31-24 of the _DSM entry are all 0 then the signal is inverted */
active_value = obj->integer.value >> 24;
pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
if (pin != agpio->pin_table[0])
dev_warn(int3472->dev, "%s %s pin number mismatch _DSM %d resource %d\n",
func, agpio->resource_source.string_ptr, pin,
agpio->pin_table[0]);
active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value);
if (!active_value)
polarity ^= GPIO_ACTIVE_LOW;
......@@ -258,7 +206,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
ret = skl_int3472_register_clock(int3472, agpio, polarity);
ret = skl_int3472_register_gpio_clock(int3472, agpio, polarity);
if (ret)
err_msg = "Failed to register clock\n";
......@@ -297,11 +245,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
LIST_HEAD(resource_list);
int ret;
/*
* No error check, because not having a sensor config is not necessarily
* a failure mode.
*/
int3472->sensor_config = skl_int3472_get_sensor_module_config(int3472);
skl_int3472_log_sensor_module_name(int3472);
ret = acpi_dev_get_resources(int3472->adev, &resource_list,
skl_int3472_handle_gpio_resources,
......@@ -311,6 +255,11 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
acpi_dev_free_resource_list(&resource_list);
/* Register _DSM based clock (no-op if a GPIO clock was already registered) */
ret = skl_int3472_register_dsm_clock(int3472);
if (ret < 0)
return ret;
int3472->gpios.dev_id = int3472->sensor_name;
gpiod_add_lookup_table(&int3472->gpios);
......@@ -356,6 +305,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev)
int3472->adev = adev;
int3472->dev = &pdev->dev;
platform_set_drvdata(pdev, int3472);
int3472->clock.imgclk_index = cldb.clock_source;
ret = skl_int3472_get_sensor_adev_and_name(&pdev->dev, &int3472->sensor,
&int3472->sensor_name);
......
......@@ -250,7 +250,7 @@ static struct i2c_driver int3472_tps68470 = {
.name = "int3472-tps68470",
.acpi_match_table = int3472_device_id,
},
.probe_new = skl_int3472_tps68470_probe,
.probe = skl_int3472_tps68470_probe,
.remove = skl_int3472_tps68470_remove,
};
module_i2c_driver(int3472_tps68470);
......
......@@ -3,8 +3,8 @@
# Intel x86 Platform-Specific Drivers
#
intel_pmc_core-y := core.o spt.o cnp.o icl.o tgl.o \
adl.o mtl.o
intel_pmc_core-y := core.o core_ssram.o spt.o cnp.o \
icl.o tgl.o adl.o mtl.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
intel_pmc_core_pltdrv-y := pltdrv.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o
......@@ -309,17 +309,21 @@ const struct pmc_reg_map adl_reg_map = {
.lpm_live_status_offset = ADL_LPM_LIVE_STATUS_OFFSET,
};
void adl_core_configure(struct pmc_dev *pmcdev)
int adl_core_init(struct pmc_dev *pmcdev)
{
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
int ret;
pmc->map = &adl_reg_map;
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
/* Due to a hardware limitation, the GBE LTR blocks PC10
* when a cable is attached. Tell the PMC to ignore it.
*/
dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n");
pmc_core_send_ltr_ignore(pmcdev, 3);
}
void adl_core_init(struct pmc_dev *pmcdev)
{
pmcdev->map = &adl_reg_map;
pmcdev->core_configure = adl_core_configure;
return 0;
}
......@@ -204,7 +204,21 @@ const struct pmc_reg_map cnp_reg_map = {
.etr3_offset = ETR3_OFFSET,
};
void cnp_core_init(struct pmc_dev *pmcdev)
int cnp_core_init(struct pmc_dev *pmcdev)
{
pmcdev->map = &cnp_reg_map;
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
int ret;
pmc->map = &cnp_reg_map;
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
/* Due to a hardware limitation, the GBE LTR blocks PC10
* when a cable is attached. Tell the PMC to ignore it.
*/
dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n");
pmc_core_send_ltr_ignore(pmcdev, 3);
return 0;
}
This diff is collapsed.
......@@ -19,6 +19,7 @@
#define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0)
#define PMC_BASE_ADDR_DEFAULT 0xFE000000
#define MAX_NUM_PMC 3
/* Sunrise Point Power Management Controller PCI Device ID */
#define SPT_PMC_PCI_DEVICE_ID 0x9d21
......@@ -249,6 +250,17 @@ enum ppfear_regs {
#define MTL_LPM_STATUS_LATCH_EN_OFFSET 0x16F8
#define MTL_LPM_STATUS_OFFSET 0x1700
#define MTL_LPM_LIVE_STATUS_OFFSET 0x175C
#define MTL_PMC_LTR_IOE_PMC 0x1C0C
#define MTL_PMC_LTR_ESE 0x1BAC
#define MTL_PMC_LTR_RESERVED 0x1BA4
#define MTL_IOE_PMC_MMIO_REG_LEN 0x23A4
#define MTL_SOCM_NUM_IP_IGN_ALLOWED 25
#define MTL_SOC_PMC_MMIO_REG_LEN 0x2708
#define MTL_PMC_LTR_SPG 0x1B74
/* Meteor Lake PGD PFET Enable Ack Status */
#define MTL_SOCM_PPFEAR_NUM_ENTRIES 8
#define MTL_IOE_PPFEAR_NUM_ENTRIES 10
extern const char *pmc_lpm_modes[];
......@@ -311,12 +323,38 @@ struct pmc_reg_map {
};
/**
* struct pmc_dev - pmc device structure
* struct pmc_info - Structure to keep pmc info
* @devid: device id of the pmc device
* @map: pointer to a pmc_reg_map struct that contains platform
* specific attributes
*/
struct pmc_info {
u16 devid;
const struct pmc_reg_map *map;
};
/**
* struct pmc - pmc private info structure
* @base_addr: contains pmc base address
* @regbase: pointer to io-remapped memory location
* @map: pointer to pmc_reg_map struct that contains platform
* specific attributes
* @lpm_req_regs: List of substate requirements
*
* pmc contains info about one power management controller device.
*/
struct pmc {
u64 base_addr;
void __iomem *regbase;
const struct pmc_reg_map *map;
u32 *lpm_req_regs;
};
/**
* struct pmc_dev - pmc device structure
* @devs: pointer to an array of pmc pointers
* @pdev: pointer to platform_device struct
* @ssram_pcidev: pointer to pci device struct for the PMC SSRAM
* @dbgfs_dir: path to debugfs interface
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available
......@@ -325,17 +363,15 @@ struct pmc_reg_map {
* @s0ix_counter: S0ix residency (step adjusted)
* @num_lpm_modes: Count of enabled modes
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
* @lpm_req_regs: List of substate requirements
* @core_configure: Function pointer to configure the platform
* @resume: Function to perform platform specific resume
*
* pmc_dev contains info about power management controller device.
*/
struct pmc_dev {
u32 base_addr;
void __iomem *regbase;
const struct pmc_reg_map *map;
struct pmc *pmcs[MAX_NUM_PMC];
struct dentry *dbgfs_dir;
struct platform_device *pdev;
struct pci_dev *ssram_pcidev;
int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */
......@@ -343,8 +379,20 @@ struct pmc_dev {
u64 s0ix_counter;
int num_lpm_modes;
int lpm_en_modes[LPM_MAX_NUM_MODES];
u32 *lpm_req_regs;
void (*core_configure)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
bool has_die_c6;
u32 die_c6_offset;
struct telem_endpoint *punit_ep;
struct pmc_info *regmap_list;
};
enum pmc_index {
PMC_IDX_MAIN,
PMC_IDX_SOC = PMC_IDX_MAIN,
PMC_IDX_IOE,
PMC_IDX_PCH,
PMC_IDX_MAX
};
extern const struct pmc_bit_map msr_map[];
......@@ -393,20 +441,64 @@ extern const struct pmc_bit_map adl_vnn_req_status_3_map[];
extern const struct pmc_bit_map adl_vnn_misc_status_map[];
extern const struct pmc_bit_map *adl_lpm_maps[];
extern const struct pmc_reg_map adl_reg_map;
extern const struct pmc_reg_map mtl_reg_map;
extern const struct pmc_bit_map mtl_socm_pfear_map[];
extern const struct pmc_bit_map *ext_mtl_socm_pfear_map[];
extern const struct pmc_bit_map mtl_socm_ltr_show_map[];
extern const struct pmc_bit_map mtl_socm_clocksource_status_map[];
extern const struct pmc_bit_map mtl_socm_power_gating_status_0_map[];
extern const struct pmc_bit_map mtl_socm_power_gating_status_1_map[];
extern const struct pmc_bit_map mtl_socm_power_gating_status_2_map[];
extern const struct pmc_bit_map mtl_socm_d3_status_0_map[];
extern const struct pmc_bit_map mtl_socm_d3_status_1_map[];
extern const struct pmc_bit_map mtl_socm_d3_status_2_map[];
extern const struct pmc_bit_map mtl_socm_d3_status_3_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_0_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_1_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_2_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_3_map[];
extern const struct pmc_bit_map mtl_socm_vnn_misc_status_map[];
extern const struct pmc_bit_map mtl_socm_signal_status_map[];
extern const struct pmc_bit_map *mtl_socm_lpm_maps[];
extern const struct pmc_reg_map mtl_socm_reg_map;
extern const struct pmc_bit_map mtl_ioep_pfear_map[];
extern const struct pmc_bit_map *ext_mtl_ioep_pfear_map[];
extern const struct pmc_bit_map mtl_ioep_ltr_show_map[];
extern const struct pmc_bit_map mtl_ioep_clocksource_status_map[];
extern const struct pmc_bit_map mtl_ioep_power_gating_status_0_map[];
extern const struct pmc_bit_map mtl_ioep_power_gating_status_1_map[];
extern const struct pmc_bit_map mtl_ioep_power_gating_status_2_map[];
extern const struct pmc_bit_map mtl_ioep_d3_status_0_map[];
extern const struct pmc_bit_map mtl_ioep_d3_status_1_map[];
extern const struct pmc_bit_map mtl_ioep_d3_status_2_map[];
extern const struct pmc_bit_map mtl_ioep_d3_status_3_map[];
extern const struct pmc_bit_map mtl_ioep_vnn_req_status_0_map[];
extern const struct pmc_bit_map mtl_ioep_vnn_req_status_1_map[];
extern const struct pmc_bit_map mtl_ioep_vnn_req_status_2_map[];
extern const struct pmc_bit_map mtl_ioep_vnn_req_status_3_map[];
extern const struct pmc_bit_map mtl_ioep_vnn_misc_status_map[];
extern const struct pmc_bit_map *mtl_ioep_lpm_maps[];
extern const struct pmc_reg_map mtl_ioep_reg_map;
extern const struct pmc_bit_map mtl_ioem_pfear_map[];
extern const struct pmc_bit_map *ext_mtl_ioem_pfear_map[];
extern const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[];
extern const struct pmc_bit_map mtl_ioem_vnn_req_status_1_map[];
extern const struct pmc_bit_map *mtl_ioem_lpm_maps[];
extern const struct pmc_reg_map mtl_ioem_reg_map;
extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value);
void spt_core_init(struct pmc_dev *pmcdev);
void cnp_core_init(struct pmc_dev *pmcdev);
void icl_core_init(struct pmc_dev *pmcdev);
void tgl_core_init(struct pmc_dev *pmcdev);
void adl_core_init(struct pmc_dev *pmcdev);
void mtl_core_init(struct pmc_dev *pmcdev);
void tgl_core_configure(struct pmc_dev *pmcdev);
void adl_core_configure(struct pmc_dev *pmcdev);
void mtl_core_configure(struct pmc_dev *pmcdev);
int pmc_core_resume_common(struct pmc_dev *pmcdev);
int get_primary_reg_base(struct pmc *pmc);
extern void pmc_core_ssram_init(struct pmc_dev *pmcdev);
int spt_core_init(struct pmc_dev *pmcdev);
int cnp_core_init(struct pmc_dev *pmcdev);
int icl_core_init(struct pmc_dev *pmcdev);
int tgl_core_init(struct pmc_dev *pmcdev);
int adl_core_init(struct pmc_dev *pmcdev);
int mtl_core_init(struct pmc_dev *pmcdev);
#define pmc_for_each_mode(i, mode, pmcdev) \
for (i = 0, mode = pmcdev->lpm_en_modes[i]; \
......
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains functions to handle discovery of PMC metrics located
* in the PMC SSRAM PCI device.
*
* Copyright (c) 2023, Intel Corporation.
* All Rights Reserved.
*
*/
#include <linux/pci.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include "core.h"
#define SSRAM_HDR_SIZE 0x100
#define SSRAM_PWRM_OFFSET 0x14
#define SSRAM_DVSEC_OFFSET 0x1C
#define SSRAM_DVSEC_SIZE 0x10
#define SSRAM_PCH_OFFSET 0x60
#define SSRAM_IOE_OFFSET 0x68
#define SSRAM_DEVID_OFFSET 0x70
static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid)
{
for (; list->map; ++list)
if (devid == list->devid)
return list->map;
return NULL;
}
static inline u64 get_base(void __iomem *addr, u32 offset)
{
return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3);
}
static void
pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base,
const struct pmc_reg_map *reg_map, int pmc_index)
{
struct pmc *pmc = pmcdev->pmcs[pmc_index];
if (!pwrm_base)
return;
/* Memory for primary PMC has been allocated in core.c */
if (!pmc) {
pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL);
if (!pmc)
return;
}
pmc->map = reg_map;
pmc->base_addr = pwrm_base;
pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length);
if (!pmc->regbase) {
devm_kfree(&pmcdev->pdev->dev, pmc);
return;
}
pmcdev->pmcs[pmc_index] = pmc;
}
static void
pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, void __iomem *ssram, u32 offset,
int pmc_idx)
{
u64 pwrm_base;
u16 devid;
if (pmc_idx != PMC_IDX_SOC) {
u64 ssram_base = get_base(ssram, offset);
if (!ssram_base)
return;
ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
if (!ssram)
return;
}
pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
devid = readw(ssram + SSRAM_DEVID_OFFSET);
if (pmcdev->regmap_list) {
const struct pmc_reg_map *map;
map = pmc_core_find_regmap(pmcdev->regmap_list, devid);
if (map)
pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx);
}
if (pmc_idx != PMC_IDX_SOC)
iounmap(ssram);
}
void pmc_core_ssram_init(struct pmc_dev *pmcdev)
{
void __iomem *ssram;
struct pci_dev *pcidev;
u64 ssram_base;
int ret;
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2));
if (!pcidev)
goto out;
ret = pcim_enable_device(pcidev);
if (ret)
goto release_dev;
ssram_base = pcidev->resource[0].start;
ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
if (!ssram)
goto disable_dev;
pmcdev->ssram_pcidev = pcidev;
pmc_core_ssram_get_pmc(pmcdev, ssram, 0, PMC_IDX_SOC);
pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_IOE_OFFSET, PMC_IDX_IOE);
pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_PCH_OFFSET, PMC_IDX_PCH);
iounmap(ssram);
out:
return;
disable_dev:
pci_disable_device(pcidev);
release_dev:
pci_dev_put(pcidev);
}
......@@ -50,7 +50,10 @@ const struct pmc_reg_map icl_reg_map = {
.etr3_offset = ETR3_OFFSET,
};
void icl_core_init(struct pmc_dev *pmcdev)
int icl_core_init(struct pmc_dev *pmcdev)
{
pmcdev->map = &icl_reg_map;
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
pmc->map = &icl_reg_map;
return get_primary_reg_base(pmc);
}
This diff is collapsed.
......@@ -134,7 +134,10 @@ const struct pmc_reg_map spt_reg_map = {
.pm_vric1_offset = SPT_PMC_VRIC1_OFFSET,
};
void spt_core_init(struct pmc_dev *pmcdev)
int spt_core_init(struct pmc_dev *pmcdev)
{
pmcdev->map = &spt_reg_map;
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
pmc->map = &spt_reg_map;
return get_primary_reg_base(pmc);
}
......@@ -208,7 +208,8 @@ const struct pmc_reg_map tgl_reg_map = {
void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev)
{
struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
const int num_maps = pmcdev->map->lpm_num_maps;
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
const int num_maps = pmc->map->lpm_num_maps;
u32 lpm_size = LPM_MAX_NUM_MODES * num_maps * 4;
union acpi_object *out_obj;
struct acpi_device *adev;
......@@ -246,24 +247,28 @@ void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev)
goto free_acpi_obj;
memcpy(lpm_req_regs, addr, lpm_size);
pmcdev->lpm_req_regs = lpm_req_regs;
pmc->lpm_req_regs = lpm_req_regs;
free_acpi_obj:
ACPI_FREE(out_obj);
}
void tgl_core_configure(struct pmc_dev *pmcdev)
int tgl_core_init(struct pmc_dev *pmcdev)
{
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
int ret;
pmc->map = &tgl_reg_map;
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
pmc_core_get_tgl_lpm_reqs(pmcdev->pdev);
/* Due to a hardware limitation, the GBE LTR blocks PC10
* when a cable is attached. Tell the PMC to ignore it.
*/
dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n");
pmc_core_send_ltr_ignore(pmcdev, 3);
}
void tgl_core_init(struct pmc_dev *pmcdev)
{
pmcdev->map = &tgl_reg_map;
pmcdev->core_configure = tgl_core_configure;
return 0;
}
......@@ -829,6 +829,7 @@ void isst_if_cdev_unregister(int device_type)
{
isst_misc_unreg();
mutex_lock(&punit_misc_dev_open_lock);
punit_callbacks[device_type].def_ioctl = NULL;
punit_callbacks[device_type].registered = 0;
if (device_type == ISST_IF_DEV_MBOX)
isst_delete_hash();
......
......@@ -1414,6 +1414,8 @@ int tpmi_sst_init(void)
ret = isst_if_cdev_register(ISST_IF_DEV_TPMI, &cb);
if (ret)
kfree(isst_common.sst_inst);
else
++isst_core_usage_count;
init_done:
mutex_unlock(&isst_tpmi_dev_lock);
return ret;
......
......@@ -222,7 +222,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
snprintf(feature_id_name, sizeof(feature_id_name), "tpmi-%s", name);
for (i = 0, tmp = res; i < pfs->pfs_header.num_entries; i++, tmp++) {
u64 entry_size_bytes = pfs->pfs_header.entry_size * 4;
u64 entry_size_bytes = pfs->pfs_header.entry_size * sizeof(u32);
tmp->start = pfs->vsec_offset + entry_size_bytes * i;
tmp->end = tmp->start + entry_size_bytes - 1;
......@@ -277,7 +277,7 @@ static int tpmi_process_info(struct intel_tpmi_info *tpmi_info,
void __iomem *info_mem;
info_mem = ioremap(pfs->vsec_offset + TPMI_INFO_BUS_INFO_OFFSET,
pfs->pfs_header.entry_size * 4 - TPMI_INFO_BUS_INFO_OFFSET);
pfs->pfs_header.entry_size * sizeof(u32) - TPMI_INFO_BUS_INFO_OFFSET);
if (!info_mem)
return -ENOMEM;
......@@ -308,6 +308,8 @@ static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, i
return 0;
}
#define TPMI_CAP_OFFSET_UNIT 1024
static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
{
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
......@@ -354,7 +356,7 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
if (!pfs_start)
pfs_start = res_start;
pfs->pfs_header.cap_offset *= 1024;
pfs->pfs_header.cap_offset *= TPMI_CAP_OFFSET_UNIT;
pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset;
......
......@@ -6,9 +6,13 @@
menu "Intel Uncore Frequency Control"
depends on X86_64 || COMPILE_TEST
config INTEL_UNCORE_FREQ_CONTROL_TPMI
tristate
config INTEL_UNCORE_FREQ_CONTROL
tristate "Intel Uncore frequency control driver"
depends on X86_64
select INTEL_UNCORE_FREQ_CONTROL_TPMI if INTEL_TPMI
help
This driver allows control of Uncore frequency limits on
supported server platforms.
......
......@@ -7,3 +7,5 @@ obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency.o
intel-uncore-frequency-y := uncore-frequency.o
obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency-common.o
intel-uncore-frequency-common-y := uncore-frequency-common.o
obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL_TPMI) += intel-uncore-frequency-tpmi.o
intel-uncore-frequency-tpmi-y := uncore-frequency-tpmi.o
......@@ -16,11 +16,34 @@ static struct kobject *uncore_root_kobj;
/* uncore instance count */
static int uncore_instance_count;
static DEFINE_IDA(intel_uncore_ida);
/* callbacks for actual HW read/write */
static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max);
static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);
static ssize_t show_domain_id(struct device *dev, struct device_attribute *attr, char *buf)
{
struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_dev_attr);
return sprintf(buf, "%u\n", data->domain_id);
}
static ssize_t show_fabric_cluster_id(struct device *dev, struct device_attribute *attr, char *buf)
{
struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_dev_attr);
return sprintf(buf, "%u\n", data->cluster_id);
}
static ssize_t show_package_id(struct device *dev, struct device_attribute *attr, char *buf)
{
struct uncore_data *data = container_of(attr, struct uncore_data, package_id_dev_attr);
return sprintf(buf, "%u\n", data->package_id);
}
static ssize_t show_min_max_freq_khz(struct uncore_data *data,
char *buf, int min_max)
{
......@@ -161,6 +184,15 @@ static int create_attr_group(struct uncore_data *data, char *name)
init_attribute_ro(initial_max_freq_khz);
init_attribute_root_ro(current_freq_khz);
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
init_attribute_root_ro(domain_id);
data->uncore_attrs[index++] = &data->domain_id_dev_attr.attr;
init_attribute_root_ro(fabric_cluster_id);
data->uncore_attrs[index++] = &data->fabric_cluster_id_dev_attr.attr;
init_attribute_root_ro(package_id);
data->uncore_attrs[index++] = &data->package_id_dev_attr.attr;
}
data->uncore_attrs[index++] = &data->max_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr;
......@@ -191,12 +223,24 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu)
goto uncore_unlock;
}
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
ret = ida_alloc(&intel_uncore_ida, GFP_KERNEL);
if (ret < 0)
goto uncore_unlock;
data->instance_id = ret;
sprintf(data->name, "uncore%02d", ret);
} else {
sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
}
uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz);
ret = create_attr_group(data, data->name);
if (!ret) {
if (ret) {
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
ida_free(&intel_uncore_ida, data->instance_id);
} else {
data->control_cpu = cpu;
data->valid = true;
}
......@@ -214,6 +258,9 @@ void uncore_freq_remove_die_entry(struct uncore_data *data)
delete_attr_group(data, data->name);
data->control_cpu = -1;
data->valid = false;
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
ida_free(&intel_uncore_ida, data->instance_id);
mutex_unlock(&uncore_lock);
}
EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
......
......@@ -21,6 +21,9 @@
* @valid: Mark the data valid/invalid
* @package_id: Package id for this instance
* @die_id: Die id for this instance
* @domain_id: Power domain id for this instance
* @cluster_id: cluster id in a domain
* @instance_id: Unique instance id to append to directory name
* @name: Sysfs entry name for this instance
* @uncore_attr_group: Attribute group storage
* @max_freq_khz_dev_attr: Storage for device attribute max_freq_khz
......@@ -28,6 +31,9 @@
* @initial_max_freq_khz_dev_attr: Storage for device attribute initial_max_freq_khz
* @initial_min_freq_khz_dev_attr: Storage for device attribute initial_min_freq_khz
* @current_freq_khz_dev_attr: Storage for device attribute current_freq_khz
* @domain_id_dev_attr: Storage for device attribute domain_id
* @fabric_cluster_id_dev_attr: Storage for device attribute fabric_cluster_id
* @package_id_dev_attr: Storage for device attribute package_id
* @uncore_attrs: Attribute storage for group creation
*
* This structure is used to encapsulate all data related to uncore sysfs
......@@ -41,6 +47,9 @@ struct uncore_data {
bool valid;
int package_id;
int die_id;
int domain_id;
int cluster_id;
int instance_id;
char name[32];
struct attribute_group uncore_attr_group;
......@@ -49,9 +58,14 @@ struct uncore_data {
struct device_attribute initial_max_freq_khz_dev_attr;
struct device_attribute initial_min_freq_khz_dev_attr;
struct device_attribute current_freq_khz_dev_attr;
struct attribute *uncore_attrs[6];
struct device_attribute domain_id_dev_attr;
struct device_attribute fabric_cluster_id_dev_attr;
struct device_attribute package_id_dev_attr;
struct attribute *uncore_attrs[9];
};
#define UNCORE_DOMAIN_ID_INVALID -1
int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int min_max),
int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq));
......
......@@ -136,6 +136,7 @@ static int uncore_event_cpu_online(unsigned int cpu)
data->package_id = topology_physical_package_id(cpu);
data->die_id = topology_die_id(cpu);
data->domain_id = UNCORE_DOMAIN_ID_INVALID;
return uncore_freq_add_entry(data, cpu);
}
......
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