mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
platform-drivers-x86 for v6.11-1
Highlights: - amd/pmf: Report system state changes using existing input events - asus-wmi: Zenbook 2023 camera LED disable support and fix TUF laptop keyboard RGB LED sysfs interface - dell-pc: Fan modes / platform profile support - hp-wmi: Fix platform profile switching on Omen/Victus laptops - intel/ISST: Use only TPMI interface when TPMI and legacy interfaces are available - intel/pmc: LTR restore support to pair with LTR ignore - intel/tpmi: Performance Limit Reasons (PLR) and APIC <-> Punit CPU numbering mapping support - WMI: driver override support and docs improvements - lenovo-yoga-c630: Support for EC (platform/arm64) - platform/arm64: Fix build with COMPILE_TEST (broke after addition of C630) - tools: Intel Speed Select Turbo Ratio Limit fix - Miscellaneous cleanups / refactoring / improvements The following is an automated shortlog grouped by driver: amd/pmf: - Remove update system state document - Use existing input event codes to update system states - Use memdup_user() arm64: - add Lenovo Yoga C630 WOS EC driver - build drivers even on non-ARM64 platforms - EC_ACER_ASPIRE1 should depend on ARCH_QCOM - EC_LENOVO_YOGA_C630 should depend on ARCH_QCOM arm64: lenovo-yoga-c630: - select AUXILIARY_BUS asus-tf103c-dock: - Use 2-argument strscpy() asus-wmi: - fix TUF laptop RGB variant - support the disable camera LED on F10 of Zenbook 2023 dell-pc: - avoid double free and invalid unregistration - Implement platform_profile dell-smbios: - Add helper for checking supported class - Move request functions for reuse Docs/admin-guide: - Remove pmf leftover reference from the index doc: TPMI: - Add entry for Performance Limit Reasons dt-bindings: platform: - Add Lenovo Yoga C630 EC hp: hp-bioscfg: - Use 2-argument strscpy() hp-wmi: - Fix implementation of the platform_profile_omen_get function - Fix platform profile option switch bug on Omen and Victus laptops ideapad-laptop: - use cleanup.h intel: chtwc_int33fe: - Use 2-argument strscpy() intel/ifs: - Switch to new Intel CPU model defines intel_ips: - Switch to new Intel CPU model defines intel/pmc: - Add support to show ltr_ignore value - Add support to undo ltr_ignore - Convert index variables to be unsigned - Move pmc assignment closer to first usage - Remove unneeded min_t check - Simplify mutex usage with cleanup helpers - Switch to new Intel CPU model defines - Use DEFINE_SHOW_STORE_ATTRIBUTE macro - Use the Elvis operator - Use the return value of pmc_core_send_msg intel_scu_wdt: - Switch to new Intel CPU model defines intel_speed_select_if: - Switch to new Intel CPU model defines intel_telemetry: - Switch to new Intel CPU model defines intel/tpmi: - Add API to get debugfs root - Add new auxiliary driver for performance limits - Add support for performance limit reasons intel: - TPMI domain id and CPU mapping intel/tpmi/plr: - Add support for the plr mailbox - Fix output in plr_print_bits() intel_turbo_max_3: - Switch to new Intel CPU model defines intel-uncore-freq: - Get rid of magic min_max argument - Get rid of magic values - Get rid of uncore_read_freq driver API - Re-arrange bit masks - Rename the sysfs helper macro names - Switch to new Intel CPU model defines - Use generic helpers for current frequency - Use uncore_index with read_control_freq ISST: - Add model specific loading for common module - Avoid some SkyLake server models - Use only TPMI interface when present p2sb: - Switch to new Intel CPU model defines serial-multi-instantiate: - Use 2-argument strscpy() think-lmi: - Use 2-argument strscpy() thinkpad_acpi: - Use 2-argument strscpy() tools/power/x86/intel-speed-select: - Set TRL MSR in 100 MHz units - v1.20 release wmi: - Add bus ABI documentation - Add driver_override support x86/platform/atom: - Switch to new Intel CPU model defines Merges: - Merge branch 'pdx86/platform-drivers-x86-lenovo-c630' into review-ilpo - Merge branch 'pdx86/platform-drivers-x86-lenovo-c630' into review-ilpo - Merge branch 'pdx86/platform-drivers-x86-lenovo-c630' into review-ilpo - Merge remote-tracking branch 'intel-speed-select/intel-sst' into review-ilpo -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSCSUwRdwTNL2MhaBlZrE9hU+XOMQUCZpZIdQAKCRBZrE9hU+XO MbIEAQCMVjDuOJSSuS2u7/iVb41Q3+kjP6X0CmSpf8dmt3rH0gD/Z9Qynw6ArRY4 PPHY25ur8kPtwtyxHfCMcar6ESpztwU= =L2LD -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v6.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 Pull x86 platform driver updates from Ilpo Järvinen: - amd/pmf: Report system state changes using existing input events - asus-wmi: Zenbook 2023 camera LED disable support and fix TUF laptop keyboard RGB LED sysfs interface - dell-pc: Fan modes / platform profile support - hp-wmi: Fix platform profile switching on Omen/Victus laptops - intel/ISST: Use only TPMI interface when TPMI and legacy interfaces are available - intel/pmc: LTR restore support to pair with LTR ignore - intel/tpmi: Performance Limit Reasons (PLR) and APIC <-> Punit CPU numbering mapping support - WMI: driver override support and docs improvements - lenovo-yoga-c630: Support for EC (platform/arm64) - platform/arm64: Fix build with COMPILE_TEST (broke after addition of C630) - tools: Intel Speed Select Turbo Ratio Limit fix - Miscellaneous cleanups / refactoring / improvements * tag 'platform-drivers-x86-v6.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (65 commits) platform/x86: asus-wmi: fix TUF laptop RGB variant platform/x86/intel/tpmi/plr: Fix output in plr_print_bits() Docs/admin-guide: Remove pmf leftover reference from the index platform/x86: ideapad-laptop: use cleanup.h platform/x86: hp-wmi: Fix implementation of the platform_profile_omen_get function platform: arm64: EC_LENOVO_YOGA_C630 should depend on ARCH_QCOM platform: arm64: EC_ACER_ASPIRE1 should depend on ARCH_QCOM platform/x86/amd/pmf: Remove update system state document platform/x86/amd/pmf: Use existing input event codes to update system states platform/x86: hp-wmi: Fix platform profile option switch bug on Omen and Victus laptops platform/x86:intel/pmc: Add support to undo ltr_ignore platform/x86:intel/pmc: Use the Elvis operator platform/x86:intel/pmc: Use DEFINE_SHOW_STORE_ATTRIBUTE macro platform/x86:intel/pmc: Remove unneeded min_t check platform/x86:intel/pmc: Add support to show ltr_ignore value platform/x86:intel/pmc: Move pmc assignment closer to first usage platform/x86:intel/pmc: Convert index variables to be unsigned platform/x86:intel/pmc: Simplify mutex usage with cleanup helpers platform/x86:intel/pmc: Use the return value of pmc_core_send_msg tools/power/x86/intel-speed-select: v1.20 release ...
This commit is contained in:
commit
a5cb6b2bbf
@ -29,3 +29,12 @@ Example:
|
||||
echo 0,0x20,0xff > mem_write
|
||||
echo 1,64,64 > mem_write
|
||||
Users: Debugging, any user space test suite
|
||||
|
||||
What: /sys/kernel/debug/tpmi-<n>/plr/domain<n>/status
|
||||
Date: Aug 2024
|
||||
KernelVersion: 6.11
|
||||
Contact: Tero Kristo <tero.kristo@linux.intel.com>
|
||||
Description:
|
||||
Shows the currently active Performance Limit Reasons for die level and the
|
||||
individual CPUs under the die. The contents of this file are sticky, and
|
||||
clearing all the statuses can be done by writing "0\n" to this file.
|
||||
|
81
Documentation/ABI/testing/sysfs-bus-wmi
Normal file
81
Documentation/ABI/testing/sysfs-bus-wmi
Normal file
@ -0,0 +1,81 @@
|
||||
What: /sys/bus/wmi/devices/.../driver_override
|
||||
Date: February 2024
|
||||
Contact: Armin Wolf <W_Armin@gmx.de>
|
||||
Description:
|
||||
This file allows the driver for a device to be specified which
|
||||
will override standard ID table matching.
|
||||
When specified, only a driver with a name matching the value
|
||||
written to driver_override will have an opportunity to bind
|
||||
to the device.
|
||||
The override is specified by writing a string to the
|
||||
driver_override file (echo wmi-event-dummy > driver_override).
|
||||
The override may be cleared with an empty string (echo > \
|
||||
driver_override) which returns the device to standard matching
|
||||
rules binding.
|
||||
Writing to driver_override does not automatically unbind the
|
||||
device from its current driver or make any attempt to automatically
|
||||
load the specified driver. If no driver with a matching name is
|
||||
currently loaded in the kernel, the device will not bind to any
|
||||
driver.
|
||||
This also allows devices to opt-out of driver binding using a
|
||||
driver_override name such as "none". Only a single driver may be
|
||||
specified in the override, there is no support for parsing delimiters.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../modalias
|
||||
Date: November 20:15
|
||||
Contact: Andy Lutomirski <luto@kernel.org>
|
||||
Description:
|
||||
This file contains the MODALIAS value emitted by uevent for a
|
||||
given WMI device.
|
||||
|
||||
Format: wmi:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../guid
|
||||
Date: November 2015
|
||||
Contact: Andy Lutomirski <luto@kernel.org>
|
||||
Description:
|
||||
This file contains the GUID used to match WMI devices to
|
||||
compatible WMI drivers. This GUID is not necessarily unique
|
||||
inside a given machine, it is solely used to identify the
|
||||
interface exposed by a given WMI device.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../object_id
|
||||
Date: November 2015
|
||||
Contact: Andy Lutomirski <luto@kernel.org>
|
||||
Description:
|
||||
This file contains the WMI object ID used internally to construct
|
||||
the ACPI method names used by non-event WMI devices. It contains
|
||||
two ASCII letters.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../notify_id
|
||||
Date: November 2015
|
||||
Contact: Andy Lutomirski <luto@kernel.org>
|
||||
Description:
|
||||
This file contains the WMI notify ID used internally to map ACPI
|
||||
events to WMI event devices. It contains two ASCII letters.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../instance_count
|
||||
Date: November 2015
|
||||
Contact: Andy Lutomirski <luto@kernel.org>
|
||||
Description:
|
||||
This file contains the number of WMI object instances being
|
||||
present on a given WMI device. It contains a non-negative
|
||||
number.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../expensive
|
||||
Date: November 2015
|
||||
Contact: Andy Lutomirski <luto@kernel.org>
|
||||
Description:
|
||||
This file contains a boolean flag signaling if interacting with
|
||||
the given WMI device will consume significant CPU resources.
|
||||
The WMI driver core will take care of enabling/disabling such
|
||||
WMI devices.
|
||||
|
||||
What: /sys/bus/wmi/devices/.../setable
|
||||
Date: May 2017
|
||||
Contact: Darren Hart (VMware) <dvhart@infradead.org>
|
||||
Description:
|
||||
This file contains a boolean flags signaling the data block
|
||||
aassociated with the given WMI device is writable. If the
|
||||
given WMI device is not associated with a data block, then
|
||||
this file will not exist.
|
@ -121,7 +121,6 @@ configure specific aspects of kernel behavior to your liking.
|
||||
parport
|
||||
perf-security
|
||||
pm/index
|
||||
pmf
|
||||
pnp
|
||||
rapidio
|
||||
RAS/index
|
||||
|
@ -1,24 +0,0 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Set udev rules for PMF Smart PC Builder
|
||||
---------------------------------------
|
||||
|
||||
AMD PMF(Platform Management Framework) Smart PC Solution builder has to set the system states
|
||||
like S0i3, Screen lock, hibernate etc, based on the output actions provided by the PMF
|
||||
TA (Trusted Application).
|
||||
|
||||
In order for this to work the PMF driver generates a uevent for userspace to react to. Below are
|
||||
sample udev rules that can facilitate this experience when a machine has PMF Smart PC solution builder
|
||||
enabled.
|
||||
|
||||
Please add the following line(s) to
|
||||
``/etc/udev/rules.d/99-local.rules``::
|
||||
|
||||
DRIVERS=="amd-pmf", ACTION=="change", ENV{EVENT_ID}=="0", RUN+="/usr/bin/systemctl suspend"
|
||||
DRIVERS=="amd-pmf", ACTION=="change", ENV{EVENT_ID}=="1", RUN+="/usr/bin/systemctl hibernate"
|
||||
DRIVERS=="amd-pmf", ACTION=="change", ENV{EVENT_ID}=="2", RUN+="/bin/loginctl lock-sessions"
|
||||
|
||||
EVENT_ID values:
|
||||
0= Put the system to S0i3/S2Idle
|
||||
1= Put the system to hibernate
|
||||
2= Lock the screen
|
@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/platform/lenovo,yoga-c630-ec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Lenovo Yoga C630 Embedded Controller.
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <andersson@kernel.org>
|
||||
|
||||
description:
|
||||
The Qualcomm Snapdragon-based Lenovo Yoga C630 has an Embedded Controller
|
||||
(EC) which handles things such as battery and USB Type-C. This binding
|
||||
describes the interface, on an I2C bus, to this EC.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: lenovo,yoga-c630-ec
|
||||
|
||||
reg:
|
||||
const: 0x70
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
'^connector@[01]$':
|
||||
$ref: /schemas/connector/usb-connector.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |+
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c1 {
|
||||
clock-frequency = <400000>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
embedded-controller@70 {
|
||||
compatible = "lenovo,yoga-c630-ec";
|
||||
reg = <0x70>;
|
||||
|
||||
interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
connector@0 {
|
||||
compatible = "usb-c-connector";
|
||||
reg = <0>;
|
||||
power-role = "source";
|
||||
data-role = "host";
|
||||
};
|
||||
|
||||
connector@1 {
|
||||
compatible = "usb-c-connector";
|
||||
reg = <1>;
|
||||
power-role = "source";
|
||||
data-role = "host";
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -392,6 +392,7 @@ ACPI WMI DRIVER
|
||||
M: Armin Wolf <W_Armin@gmx.de>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-wmi
|
||||
F: Documentation/driver-api/wmi.rst
|
||||
F: Documentation/wmi/
|
||||
F: drivers/platform/x86/wmi.c
|
||||
@ -6194,6 +6195,12 @@ F: Documentation/ABI/obsolete/procfs-i8k
|
||||
F: drivers/hwmon/dell-smm-hwmon.c
|
||||
F: include/uapi/linux/i8k.h
|
||||
|
||||
DELL PC DRIVER
|
||||
M: Lyndon Sanche <lsanche@lyndeno.ca>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/dell/dell-pc.c
|
||||
|
||||
DELL REMOTE BIOS UPDATE DRIVER
|
||||
M: Stuart Hayes <stuart.w.hayes@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
|
@ -165,14 +165,13 @@ static void punit_s2idle_check_register(struct punit_device *punit_device) {}
|
||||
static void punit_s2idle_check_unregister(void) {}
|
||||
#endif
|
||||
|
||||
#define X86_MATCH(model, data) \
|
||||
X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, INTEL_FAM6_##model, \
|
||||
X86_FEATURE_MWAIT, data)
|
||||
#define X86_MATCH(vfm, data) \
|
||||
X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_MWAIT, data)
|
||||
|
||||
static const struct x86_cpu_id intel_punit_cpu_ids[] = {
|
||||
X86_MATCH(ATOM_SILVERMONT, &punit_device_byt),
|
||||
X86_MATCH(ATOM_SILVERMONT_MID, &punit_device_tng),
|
||||
X86_MATCH(ATOM_AIRMONT, &punit_device_cht),
|
||||
X86_MATCH(INTEL_ATOM_SILVERMONT, &punit_device_byt),
|
||||
X86_MATCH(INTEL_ATOM_SILVERMONT_MID, &punit_device_tng),
|
||||
X86_MATCH(INTEL_ATOM_AIRMONT, &punit_device_cht),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids);
|
||||
|
@ -12,4 +12,4 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
|
||||
obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
|
||||
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
|
||||
obj-$(CONFIG_ARM64) += arm64/
|
||||
obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/
|
||||
|
@ -18,6 +18,7 @@ if ARM64_PLATFORM_DEVICES
|
||||
|
||||
config EC_ACER_ASPIRE1
|
||||
tristate "Acer Aspire 1 Embedded Controller driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on I2C
|
||||
depends on DRM
|
||||
depends on POWER_SUPPLY
|
||||
@ -32,4 +33,20 @@ config EC_ACER_ASPIRE1
|
||||
laptop where this information is not properly exposed via the
|
||||
standard ACPI devices.
|
||||
|
||||
config EC_LENOVO_YOGA_C630
|
||||
tristate "Lenovo Yoga C630 Embedded Controller driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on I2C
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
Driver for the Embedded Controller in the Qualcomm Snapdragon-based
|
||||
Lenovo Yoga C630, which provides battery and power adapter
|
||||
information.
|
||||
|
||||
This driver provides battery and AC status support for the mentioned
|
||||
laptop where this information is not properly exposed via the
|
||||
standard ACPI devices.
|
||||
|
||||
Say M or Y here to include this support.
|
||||
|
||||
endif # ARM64_PLATFORM_DEVICES
|
||||
|
@ -6,3 +6,4 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
|
||||
obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
|
||||
|
291
drivers/platform/arm64/lenovo-yoga-c630.c
Normal file
291
drivers/platform/arm64/lenovo-yoga-c630.c
Normal file
@ -0,0 +1,291 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022-2024, Linaro Ltd
|
||||
* Authors:
|
||||
* Bjorn Andersson
|
||||
* Dmitry Baryshkov
|
||||
*/
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/lenovo-yoga-c630.h>
|
||||
|
||||
#define LENOVO_EC_RESPONSE_REG 0x01
|
||||
#define LENOVO_EC_REQUEST_REG 0x02
|
||||
|
||||
#define LENOVO_EC_UCSI_WRITE 0x20
|
||||
#define LENOVO_EC_UCSI_READ 0x21
|
||||
|
||||
#define LENOVO_EC_READ_REG 0xb0
|
||||
#define LENOVO_EC_REQUEST_NEXT_EVENT 0x84
|
||||
|
||||
#define LENOVO_EC_UCSI_VERSION 0x20
|
||||
|
||||
struct yoga_c630_ec {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
struct blocking_notifier_head notifier_list;
|
||||
};
|
||||
|
||||
static int yoga_c630_ec_request(struct yoga_c630_ec *ec, u8 *req, size_t req_len,
|
||||
u8 *resp, size_t resp_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ec->lock);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_REQUEST_REG,
|
||||
req_len, req);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_RESPONSE_REG,
|
||||
resp_len, resp);
|
||||
}
|
||||
|
||||
int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr)
|
||||
{
|
||||
u8 req[2] = { LENOVO_EC_READ_REG, };
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
guard(mutex)(&ec->lock);
|
||||
|
||||
req[1] = addr;
|
||||
ret = yoga_c630_ec_request(ec, req, sizeof(req), &val, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_read8);
|
||||
|
||||
int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr)
|
||||
{
|
||||
u8 req[2] = { LENOVO_EC_READ_REG, };
|
||||
int ret;
|
||||
u8 msb;
|
||||
u8 lsb;
|
||||
|
||||
/* don't overflow the address */
|
||||
if (addr == 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&ec->lock);
|
||||
|
||||
req[1] = addr;
|
||||
ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
req[1] = addr + 1;
|
||||
ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return msb << 8 | lsb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_read16);
|
||||
|
||||
u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec)
|
||||
{
|
||||
u8 req[3] = { 0xb3, 0xf2, };
|
||||
int ret;
|
||||
u8 msb;
|
||||
u8 lsb;
|
||||
|
||||
guard(mutex)(&ec->lock);
|
||||
|
||||
req[2] = LENOVO_EC_UCSI_VERSION;
|
||||
ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
req[2] = LENOVO_EC_UCSI_VERSION + 1;
|
||||
ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return msb << 8 | lsb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_get_version);
|
||||
|
||||
int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec,
|
||||
const u8 req[YOGA_C630_UCSI_WRITE_SIZE])
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ec->lock);
|
||||
ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_UCSI_WRITE,
|
||||
YOGA_C630_UCSI_WRITE_SIZE, req);
|
||||
mutex_unlock(&ec->lock);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_write);
|
||||
|
||||
int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec,
|
||||
u8 resp[YOGA_C630_UCSI_READ_SIZE])
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ec->lock);
|
||||
ret = i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_UCSI_READ,
|
||||
YOGA_C630_UCSI_READ_SIZE, resp);
|
||||
mutex_unlock(&ec->lock);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_read);
|
||||
|
||||
static irqreturn_t yoga_c630_ec_thread_intr(int irq, void *data)
|
||||
{
|
||||
u8 req[] = { LENOVO_EC_REQUEST_NEXT_EVENT };
|
||||
struct yoga_c630_ec *ec = data;
|
||||
u8 event;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ec->lock);
|
||||
ret = yoga_c630_ec_request(ec, req, sizeof(req), &event, 1);
|
||||
mutex_unlock(&ec->lock);
|
||||
if (ret < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
blocking_notifier_call_chain(&ec->notifier_list, event, ec);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* yoga_c630_ec_register_notify - Register a notifier callback for EC events.
|
||||
* @ec: Yoga C630 EC
|
||||
* @nb: Notifier block pointer to register
|
||||
*
|
||||
* Return: 0 on success or negative error code.
|
||||
*/
|
||||
int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&ec->notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_register_notify);
|
||||
|
||||
/**
|
||||
* yoga_c630_ec_unregister_notify - Unregister notifier callback for EC events.
|
||||
* @ec: Yoga C630 EC
|
||||
* @nb: Notifier block pointer to unregister
|
||||
*
|
||||
* Unregister a notifier callback that was previously registered with
|
||||
* yoga_c630_ec_register_notify().
|
||||
*/
|
||||
void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&ec->notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify);
|
||||
|
||||
static void yoga_c630_aux_release(struct device *dev)
|
||||
{
|
||||
struct auxiliary_device *adev = to_auxiliary_dev(dev);
|
||||
|
||||
kfree(adev);
|
||||
}
|
||||
|
||||
static void yoga_c630_aux_remove(void *data)
|
||||
{
|
||||
struct auxiliary_device *adev = data;
|
||||
|
||||
auxiliary_device_delete(adev);
|
||||
auxiliary_device_uninit(adev);
|
||||
}
|
||||
|
||||
static int yoga_c630_aux_init(struct device *parent, const char *name,
|
||||
struct yoga_c630_ec *ec)
|
||||
{
|
||||
struct auxiliary_device *adev;
|
||||
int ret;
|
||||
|
||||
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
|
||||
if (!adev)
|
||||
return -ENOMEM;
|
||||
|
||||
adev->name = name;
|
||||
adev->id = 0;
|
||||
adev->dev.parent = parent;
|
||||
adev->dev.release = yoga_c630_aux_release;
|
||||
adev->dev.platform_data = ec;
|
||||
|
||||
ret = auxiliary_device_init(adev);
|
||||
if (ret) {
|
||||
kfree(adev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auxiliary_device_add(adev);
|
||||
if (ret) {
|
||||
auxiliary_device_uninit(adev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev);
|
||||
}
|
||||
|
||||
static int yoga_c630_ec_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct yoga_c630_ec *ec;
|
||||
int ret;
|
||||
|
||||
ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
|
||||
if (!ec)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ec->lock);
|
||||
ec->client = client;
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, client->irq,
|
||||
NULL, yoga_c630_ec_thread_intr,
|
||||
IRQF_ONESHOT, "yoga_c630_ec", ec);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "unable to request irq\n");
|
||||
|
||||
ret = yoga_c630_aux_init(dev, YOGA_C630_DEV_PSY, ec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return yoga_c630_aux_init(dev, YOGA_C630_DEV_UCSI, ec);
|
||||
}
|
||||
|
||||
|
||||
static const struct of_device_id yoga_c630_ec_of_match[] = {
|
||||
{ .compatible = "lenovo,yoga-c630-ec" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match);
|
||||
|
||||
static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = {
|
||||
{ "yoga-c630-ec", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table);
|
||||
|
||||
static struct i2c_driver yoga_c630_ec_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "yoga-c630-ec",
|
||||
.of_match_table = yoga_c630_ec_of_match
|
||||
},
|
||||
.probe = yoga_c630_ec_probe,
|
||||
.id_table = yoga_c630_ec_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(yoga_c630_ec_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Lenovo Yoga C630 Embedded Controller");
|
||||
MODULE_LICENSE("GPL");
|
@ -12,6 +12,7 @@
|
||||
#define PMF_H
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_profile.h>
|
||||
|
||||
#define POLICY_BUF_MAX_SZ 0x4b000
|
||||
@ -300,6 +301,7 @@ struct amd_pmf_dev {
|
||||
void __iomem *policy_base;
|
||||
bool smart_pc_enabled;
|
||||
u16 pmf_if_version;
|
||||
struct input_dev *pmf_idev;
|
||||
};
|
||||
|
||||
struct apmf_sps_prop_granular_v2 {
|
||||
|
@ -62,18 +62,12 @@ static void amd_pmf_prepare_args(struct amd_pmf_dev *dev, int cmd,
|
||||
param[0].u.memref.shm_offs = 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_update_uevents(struct amd_pmf_dev *dev, u16 event)
|
||||
static void amd_pmf_update_uevents(struct amd_pmf_dev *dev, u16 event)
|
||||
{
|
||||
char *envp[2] = {};
|
||||
|
||||
envp[0] = kasprintf(GFP_KERNEL, "EVENT_ID=%d", event);
|
||||
if (!envp[0])
|
||||
return -EINVAL;
|
||||
|
||||
kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
kfree(envp[0]);
|
||||
return 0;
|
||||
input_report_key(dev->pmf_idev, event, 1); /* key press */
|
||||
input_sync(dev->pmf_idev);
|
||||
input_report_key(dev->pmf_idev, event, 0); /* key release */
|
||||
input_sync(dev->pmf_idev);
|
||||
}
|
||||
|
||||
static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out)
|
||||
@ -149,7 +143,20 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
|
||||
break;
|
||||
|
||||
case PMF_POLICY_SYSTEM_STATE:
|
||||
amd_pmf_update_uevents(dev, val);
|
||||
switch (val) {
|
||||
case 0:
|
||||
amd_pmf_update_uevents(dev, KEY_SLEEP);
|
||||
break;
|
||||
case 1:
|
||||
amd_pmf_update_uevents(dev, KEY_SUSPEND);
|
||||
break;
|
||||
case 2:
|
||||
amd_pmf_update_uevents(dev, KEY_SCREENLOCK);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "Invalid PMF policy system state: %d\n", val);
|
||||
}
|
||||
|
||||
dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n",
|
||||
amd_pmf_uevent_as_str(val));
|
||||
break;
|
||||
@ -301,14 +308,9 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
|
||||
return -EINVAL;
|
||||
|
||||
/* re-alloc to the new buffer length of the policy binary */
|
||||
new_policy_buf = kzalloc(length, GFP_KERNEL);
|
||||
if (!new_policy_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(new_policy_buf, buf, length)) {
|
||||
kfree(new_policy_buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
new_policy_buf = memdup_user(buf, length);
|
||||
if (IS_ERR(new_policy_buf))
|
||||
return PTR_ERR(new_policy_buf);
|
||||
|
||||
kfree(dev->policy_buf);
|
||||
dev->policy_buf = new_policy_buf;
|
||||
@ -368,6 +370,30 @@ static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
dev->pmf_idev = devm_input_allocate_device(dev->dev);
|
||||
if (!dev->pmf_idev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->pmf_idev->name = "PMF-TA output events";
|
||||
dev->pmf_idev->phys = "amd-pmf/input0";
|
||||
|
||||
input_set_capability(dev->pmf_idev, EV_KEY, KEY_SLEEP);
|
||||
input_set_capability(dev->pmf_idev, EV_KEY, KEY_SCREENLOCK);
|
||||
input_set_capability(dev->pmf_idev, EV_KEY, KEY_SUSPEND);
|
||||
|
||||
err = input_register_device(dev->pmf_idev);
|
||||
if (err) {
|
||||
dev_err(dev->dev, "Failed to register input device: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
|
||||
{
|
||||
u32 size;
|
||||
@ -475,6 +501,10 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
|
||||
if (pb_side_load)
|
||||
amd_pmf_open_pb(dev, dev->dbgfs_dir);
|
||||
|
||||
ret = amd_pmf_register_input_device(dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@ -485,6 +515,9 @@ error:
|
||||
|
||||
void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev)
|
||||
{
|
||||
if (dev->pmf_idev)
|
||||
input_unregister_device(dev->pmf_idev);
|
||||
|
||||
if (pb_side_load && dev->esbin)
|
||||
amd_pmf_remove_pb(dev);
|
||||
|
||||
|
@ -490,7 +490,7 @@ static void tf103c_dock_enable_touchpad(struct tf103c_dock_data *dock)
|
||||
return;
|
||||
}
|
||||
|
||||
strscpy(board_info.type, "elan_i2c", I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, "elan_i2c");
|
||||
board_info.addr = TF103C_DOCK_TP_ADDR;
|
||||
board_info.dev_name = TF103C_DOCK_DEV_NAME "-tp";
|
||||
board_info.irq = dock->tp_irq;
|
||||
@ -795,7 +795,7 @@ static int tf103c_dock_probe(struct i2c_client *client)
|
||||
*/
|
||||
dock->ec_client = client;
|
||||
|
||||
strscpy(board_info.type, "tf103c-dock-intr", I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, "tf103c-dock-intr");
|
||||
board_info.addr = TF103C_DOCK_INTR_ADDR;
|
||||
board_info.dev_name = TF103C_DOCK_DEV_NAME "-intr";
|
||||
|
||||
@ -803,7 +803,7 @@ static int tf103c_dock_probe(struct i2c_client *client)
|
||||
if (IS_ERR(dock->intr_client))
|
||||
return dev_err_probe(dev, PTR_ERR(dock->intr_client), "creating intr client\n");
|
||||
|
||||
strscpy(board_info.type, "tf103c-dock-kbd", I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, "tf103c-dock-kbd");
|
||||
board_info.addr = TF103C_DOCK_KBD_ADDR;
|
||||
board_info.dev_name = TF103C_DOCK_DEV_NAME "-kbd";
|
||||
|
||||
@ -846,8 +846,8 @@ static int tf103c_dock_probe(struct i2c_client *client)
|
||||
dock->hid->vendor = 0x0b05; /* USB_VENDOR_ID_ASUSTEK */
|
||||
dock->hid->product = 0x0103; /* From TF-103-C */
|
||||
dock->hid->version = 0x0100; /* 1.0 */
|
||||
strscpy(dock->hid->name, "Asus TF103C Dock Keyboard", sizeof(dock->hid->name));
|
||||
strscpy(dock->hid->phys, dev_name(dev), sizeof(dock->hid->phys));
|
||||
strscpy(dock->hid->name, "Asus TF103C Dock Keyboard");
|
||||
strscpy(dock->hid->phys, dev_name(dev));
|
||||
|
||||
ret = hid_add_device(dock->hid);
|
||||
if (ret)
|
||||
|
@ -238,6 +238,7 @@ struct asus_wmi {
|
||||
struct led_classdev lightbar_led;
|
||||
int lightbar_led_wk;
|
||||
struct led_classdev micmute_led;
|
||||
struct led_classdev camera_led;
|
||||
struct workqueue_struct *led_workqueue;
|
||||
struct work_struct tpd_led_work;
|
||||
struct work_struct wlan_led_work;
|
||||
@ -879,10 +880,14 @@ static ssize_t kbd_rgb_mode_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct asus_wmi *asus = dev_get_drvdata(dev);
|
||||
u32 cmd, mode, r, g, b, speed;
|
||||
struct led_classdev *led;
|
||||
struct asus_wmi *asus;
|
||||
int err;
|
||||
|
||||
led = dev_get_drvdata(dev);
|
||||
asus = container_of(led, struct asus_wmi, kbd_led);
|
||||
|
||||
if (sscanf(buf, "%d %d %d %d %d %d", &cmd, &mode, &r, &g, &b, &speed) != 6)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1642,6 +1647,27 @@ static int micmute_led_set(struct led_classdev *led_cdev,
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static enum led_brightness camera_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
u32 result;
|
||||
|
||||
asus = container_of(led_cdev, struct asus_wmi, camera_led);
|
||||
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CAMERA_LED, &result);
|
||||
|
||||
return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
|
||||
}
|
||||
|
||||
static int camera_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
int state = brightness != LED_OFF;
|
||||
int err;
|
||||
|
||||
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CAMERA_LED, state, NULL);
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static void asus_wmi_led_exit(struct asus_wmi *asus)
|
||||
{
|
||||
led_classdev_unregister(&asus->kbd_led);
|
||||
@ -1649,6 +1675,7 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)
|
||||
led_classdev_unregister(&asus->wlan_led);
|
||||
led_classdev_unregister(&asus->lightbar_led);
|
||||
led_classdev_unregister(&asus->micmute_led);
|
||||
led_classdev_unregister(&asus->camera_led);
|
||||
|
||||
if (asus->led_workqueue)
|
||||
destroy_workqueue(asus->led_workqueue);
|
||||
@ -1740,6 +1767,18 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CAMERA_LED)) {
|
||||
asus->camera_led.name = "asus::camera";
|
||||
asus->camera_led.max_brightness = 1;
|
||||
asus->camera_led.brightness_get = camera_led_get;
|
||||
asus->camera_led.brightness_set_blocking = camera_led_set;
|
||||
|
||||
rv = led_classdev_register(&asus->platform_device->dev,
|
||||
&asus->camera_led);
|
||||
if (rv)
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
if (rv)
|
||||
asus_wmi_led_exit(asus);
|
||||
|
@ -91,6 +91,19 @@ config DELL_RBTN
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called dell-rbtn.
|
||||
|
||||
config DELL_PC
|
||||
tristate "Dell PC Extras"
|
||||
default m
|
||||
depends on ACPI
|
||||
depends on DMI
|
||||
depends on DELL_SMBIOS
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
help
|
||||
This driver adds support for controlling the fan modes via platform_profile
|
||||
on supported Dell systems regardless of formfactor.
|
||||
Module will simply do nothing if thermal management commands are not
|
||||
supported.
|
||||
|
||||
#
|
||||
# The DELL_SMBIOS driver depends on ACPI_WMI and/or DCDBAS if those
|
||||
# backends are selected. The "depends" line prevents a configuration
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_DCDBAS) += dcdbas.o
|
||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||
obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o
|
||||
obj-$(CONFIG_DELL_RBU) += dell_rbu.o
|
||||
obj-$(CONFIG_DELL_PC) += dell-pc.o
|
||||
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
|
||||
dell-smbios-objs := dell-smbios-base.o
|
||||
dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o
|
||||
|
@ -353,29 +353,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static void dell_fill_request(struct calling_interface_buffer *buffer,
|
||||
u32 arg0, u32 arg1, u32 arg2, u32 arg3)
|
||||
{
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer->input[0] = arg0;
|
||||
buffer->input[1] = arg1;
|
||||
buffer->input[2] = arg2;
|
||||
buffer->input[3] = arg3;
|
||||
}
|
||||
|
||||
static int dell_send_request(struct calling_interface_buffer *buffer,
|
||||
u16 class, u16 select)
|
||||
{
|
||||
int ret;
|
||||
|
||||
buffer->cmd_class = class;
|
||||
buffer->cmd_select = select;
|
||||
ret = dell_smbios_call(buffer);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
return dell_smbios_error(buffer->output[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived from information in smbios-wireless-ctl:
|
||||
*
|
||||
|
309
drivers/platform/x86/dell/dell-pc.c
Normal file
309
drivers/platform/x86/dell/dell-pc.c
Normal file
@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for Dell laptop extras
|
||||
*
|
||||
* Copyright (c) Lyndon Sanche <lsanche@lyndeno.ca>
|
||||
*
|
||||
* Based on documentation in the libsmbios package:
|
||||
* Copyright (C) 2005-2014 Dell Inc.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dell-smbios.h"
|
||||
|
||||
static const struct dmi_system_id dell_device_table[] __initconst = {
|
||||
{
|
||||
.ident = "Dell Inc.",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Dell Computer Corporation",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, dell_device_table);
|
||||
|
||||
/* Derived from smbios-thermal-ctl
|
||||
*
|
||||
* cbClass 17
|
||||
* cbSelect 19
|
||||
* User Selectable Thermal Tables(USTT)
|
||||
* cbArg1 determines the function to be performed
|
||||
* cbArg1 0x0 = Get Thermal Information
|
||||
* cbRES1 Standard return codes (0, -1, -2)
|
||||
* cbRES2, byte 0 Bitmap of supported thermal modes. A mode is supported if
|
||||
* its bit is set to 1
|
||||
* Bit 0 Balanced
|
||||
* Bit 1 Cool Bottom
|
||||
* Bit 2 Quiet
|
||||
* Bit 3 Performance
|
||||
* cbRES2, byte 1 Bitmap of supported Active Acoustic Controller (AAC) modes.
|
||||
* Each mode corresponds to the supported thermal modes in
|
||||
* byte 0. A mode is supported if its bit is set to 1.
|
||||
* Bit 0 AAC (Balanced)
|
||||
* Bit 1 AAC (Cool Bottom
|
||||
* Bit 2 AAC (Quiet)
|
||||
* Bit 3 AAC (Performance)
|
||||
* cbRes3, byte 0 Current Thermal Mode
|
||||
* Bit 0 Balanced
|
||||
* Bit 1 Cool Bottom
|
||||
* Bit 2 Quiet
|
||||
* Bit 3 Performanc
|
||||
* cbRes3, byte 1 AAC Configuration type
|
||||
* 0 Global (AAC enable/disable applies to all supported USTT modes)
|
||||
* 1 USTT mode specific
|
||||
* cbRes3, byte 2 Current Active Acoustic Controller (AAC) Mode
|
||||
* If AAC Configuration Type is Global,
|
||||
* 0 AAC mode disabled
|
||||
* 1 AAC mode enabled
|
||||
* If AAC Configuration Type is USTT mode specific (multiple bits may be set),
|
||||
* Bit 0 AAC (Balanced)
|
||||
* Bit 1 AAC (Cool Bottom
|
||||
* Bit 2 AAC (Quiet)
|
||||
* Bit 3 AAC (Performance)
|
||||
* cbRes3, byte 3 Current Fan Failure Mode
|
||||
* Bit 0 Minimal Fan Failure (at least one fan has failed, one fan working)
|
||||
* Bit 1 Catastrophic Fan Failure (all fans have failed)
|
||||
*
|
||||
* cbArg1 0x1 (Set Thermal Information), both desired thermal mode and
|
||||
* desired AAC mode shall be applied
|
||||
* cbArg2, byte 0 Desired Thermal Mode to set
|
||||
* (only one bit may be set for this parameter)
|
||||
* Bit 0 Balanced
|
||||
* Bit 1 Cool Bottom
|
||||
* Bit 2 Quiet
|
||||
* Bit 3 Performance
|
||||
* cbArg2, byte 1 Desired Active Acoustic Controller (AAC) Mode to set
|
||||
* If AAC Configuration Type is Global,
|
||||
* 0 AAC mode disabled
|
||||
* 1 AAC mode enabled
|
||||
* If AAC Configuration Type is USTT mode specific
|
||||
* (multiple bits may be set for this parameter),
|
||||
* Bit 0 AAC (Balanced)
|
||||
* Bit 1 AAC (Cool Bottom
|
||||
* Bit 2 AAC (Quiet)
|
||||
* Bit 3 AAC (Performance)
|
||||
*/
|
||||
|
||||
#define DELL_ACC_GET_FIELD GENMASK(19, 16)
|
||||
#define DELL_ACC_SET_FIELD GENMASK(11, 8)
|
||||
#define DELL_THERMAL_SUPPORTED GENMASK(3, 0)
|
||||
|
||||
static struct platform_profile_handler *thermal_handler;
|
||||
|
||||
enum thermal_mode_bits {
|
||||
DELL_BALANCED = BIT(0),
|
||||
DELL_COOL_BOTTOM = BIT(1),
|
||||
DELL_QUIET = BIT(2),
|
||||
DELL_PERFORMANCE = BIT(3),
|
||||
};
|
||||
|
||||
static int thermal_get_mode(void)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int state;
|
||||
int ret;
|
||||
|
||||
dell_fill_request(&buffer, 0x0, 0, 0, 0);
|
||||
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
|
||||
if (ret)
|
||||
return ret;
|
||||
state = buffer.output[2];
|
||||
if (state & DELL_BALANCED)
|
||||
return DELL_BALANCED;
|
||||
else if (state & DELL_COOL_BOTTOM)
|
||||
return DELL_COOL_BOTTOM;
|
||||
else if (state & DELL_QUIET)
|
||||
return DELL_QUIET;
|
||||
else if (state & DELL_PERFORMANCE)
|
||||
return DELL_PERFORMANCE;
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int thermal_get_supported_modes(int *supported_bits)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int ret;
|
||||
|
||||
dell_fill_request(&buffer, 0x0, 0, 0, 0);
|
||||
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
|
||||
/* Thermal function not supported */
|
||||
if (ret == -ENXIO) {
|
||||
*supported_bits = 0;
|
||||
return 0;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
*supported_bits = FIELD_GET(DELL_THERMAL_SUPPORTED, buffer.output[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_get_acc_mode(int *acc_mode)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int ret;
|
||||
|
||||
dell_fill_request(&buffer, 0x0, 0, 0, 0);
|
||||
ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
|
||||
if (ret)
|
||||
return ret;
|
||||
*acc_mode = FIELD_GET(DELL_ACC_GET_FIELD, buffer.output[3]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_set_mode(enum thermal_mode_bits state)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int ret;
|
||||
int acc_mode;
|
||||
|
||||
ret = thermal_get_acc_mode(&acc_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dell_fill_request(&buffer, 0x1, FIELD_PREP(DELL_ACC_SET_FIELD, acc_mode) | state, 0, 0);
|
||||
return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
|
||||
}
|
||||
|
||||
static int thermal_platform_profile_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
switch (profile) {
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
return thermal_set_mode(DELL_BALANCED);
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
return thermal_set_mode(DELL_PERFORMANCE);
|
||||
case PLATFORM_PROFILE_QUIET:
|
||||
return thermal_set_mode(DELL_QUIET);
|
||||
case PLATFORM_PROFILE_COOL:
|
||||
return thermal_set_mode(DELL_COOL_BOTTOM);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int thermal_platform_profile_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = thermal_get_mode();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ret) {
|
||||
case DELL_BALANCED:
|
||||
*profile = PLATFORM_PROFILE_BALANCED;
|
||||
break;
|
||||
case DELL_PERFORMANCE:
|
||||
*profile = PLATFORM_PROFILE_PERFORMANCE;
|
||||
break;
|
||||
case DELL_COOL_BOTTOM:
|
||||
*profile = PLATFORM_PROFILE_COOL;
|
||||
break;
|
||||
case DELL_QUIET:
|
||||
*profile = PLATFORM_PROFILE_QUIET;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_init(void)
|
||||
{
|
||||
int ret;
|
||||
int supported_modes;
|
||||
|
||||
/* If thermal commands are not supported, exit without error */
|
||||
if (!dell_smbios_class_is_supported(CLASS_INFO))
|
||||
return 0;
|
||||
|
||||
/* If thermal modes are not supported, exit without error */
|
||||
ret = thermal_get_supported_modes(&supported_modes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!supported_modes)
|
||||
return 0;
|
||||
|
||||
thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL);
|
||||
if (!thermal_handler)
|
||||
return -ENOMEM;
|
||||
thermal_handler->profile_get = thermal_platform_profile_get;
|
||||
thermal_handler->profile_set = thermal_platform_profile_set;
|
||||
|
||||
if (supported_modes & DELL_QUIET)
|
||||
set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices);
|
||||
if (supported_modes & DELL_COOL_BOTTOM)
|
||||
set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices);
|
||||
if (supported_modes & DELL_BALANCED)
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices);
|
||||
if (supported_modes & DELL_PERFORMANCE)
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices);
|
||||
|
||||
/* Clean up if failed */
|
||||
ret = platform_profile_register(thermal_handler);
|
||||
if (ret) {
|
||||
kfree(thermal_handler);
|
||||
thermal_handler = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void thermal_cleanup(void)
|
||||
{
|
||||
if (thermal_handler) {
|
||||
platform_profile_remove();
|
||||
kfree(thermal_handler);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init dell_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dmi_check_system(dell_device_table))
|
||||
return -ENODEV;
|
||||
|
||||
/* Do not fail module if thermal modes not supported, just skip */
|
||||
ret = thermal_init();
|
||||
if (ret)
|
||||
goto fail_thermal;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_thermal:
|
||||
thermal_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dell_exit(void)
|
||||
{
|
||||
thermal_cleanup();
|
||||
}
|
||||
|
||||
module_init(dell_init);
|
||||
module_exit(dell_exit);
|
||||
|
||||
MODULE_AUTHOR("Lyndon Sanche <lsanche@lyndeno.ca>");
|
||||
MODULE_DESCRIPTION("Dell PC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -77,6 +77,7 @@ static struct smbios_call call_blacklist[] = {
|
||||
/* handled by kernel: dell-laptop */
|
||||
{0x0000, CLASS_INFO, SELECT_RFKILL},
|
||||
{0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
|
||||
{0x0000, CLASS_INFO, SELECT_THERMAL_MANAGEMENT},
|
||||
};
|
||||
|
||||
struct token_range {
|
||||
@ -320,6 +321,31 @@ out_smbios_call:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_call);
|
||||
|
||||
void dell_fill_request(struct calling_interface_buffer *buffer,
|
||||
u32 arg0, u32 arg1, u32 arg2, u32 arg3)
|
||||
{
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
buffer->input[0] = arg0;
|
||||
buffer->input[1] = arg1;
|
||||
buffer->input[2] = arg2;
|
||||
buffer->input[3] = arg3;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_fill_request);
|
||||
|
||||
int dell_send_request(struct calling_interface_buffer *buffer,
|
||||
u16 class, u16 select)
|
||||
{
|
||||
int ret;
|
||||
|
||||
buffer->cmd_class = class;
|
||||
buffer->cmd_select = select;
|
||||
ret = dell_smbios_call(buffer);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
return dell_smbios_error(buffer->output[0]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_send_request);
|
||||
|
||||
struct calling_interface_token *dell_smbios_find_token(int tokenid)
|
||||
{
|
||||
int i;
|
||||
@ -356,6 +382,15 @@ void dell_laptop_call_notifier(unsigned long action, void *data)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_laptop_call_notifier);
|
||||
|
||||
bool dell_smbios_class_is_supported(u16 class)
|
||||
{
|
||||
/* Classes over 30 always unsupported */
|
||||
if (class > 30)
|
||||
return false;
|
||||
return da_supported_commands & (1 << class);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_class_is_supported);
|
||||
|
||||
static void __init parse_da_table(const struct dmi_header *dm)
|
||||
{
|
||||
/* Final token is a terminator, so we don't want to copy it */
|
||||
|
@ -19,6 +19,7 @@
|
||||
/* Classes and selects used only in kernel drivers */
|
||||
#define CLASS_KBD_BACKLIGHT 4
|
||||
#define SELECT_KBD_BACKLIGHT 11
|
||||
#define SELECT_THERMAL_MANAGEMENT 19
|
||||
|
||||
/* Tokens used in kernel drivers, any of these
|
||||
* should be filtered from userspace access
|
||||
@ -64,6 +65,11 @@ int dell_smbios_call_filter(struct device *d,
|
||||
struct calling_interface_buffer *buffer);
|
||||
int dell_smbios_call(struct calling_interface_buffer *buffer);
|
||||
|
||||
void dell_fill_request(struct calling_interface_buffer *buffer,
|
||||
u32 arg0, u32 arg1, u32 arg2, u32 arg3);
|
||||
int dell_send_request(struct calling_interface_buffer *buffer,
|
||||
u16 class, u16 select);
|
||||
|
||||
struct calling_interface_token *dell_smbios_find_token(int tokenid);
|
||||
|
||||
enum dell_laptop_notifier_actions {
|
||||
@ -73,6 +79,7 @@ enum dell_laptop_notifier_actions {
|
||||
int dell_laptop_register_notifier(struct notifier_block *nb);
|
||||
int dell_laptop_unregister_notifier(struct notifier_block *nb);
|
||||
void dell_laptop_call_notifier(unsigned long action, void *data);
|
||||
bool dell_smbios_class_is_supported(u16 class);
|
||||
|
||||
/* for the supported backends */
|
||||
#ifdef CONFIG_DELL_SMBIOS_WMI
|
||||
|
@ -40,6 +40,7 @@ config HP_WMI
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select POWER_SUPPLY
|
||||
select INPUT_SPARSEKMAP
|
||||
select ACPI_PLATFORM_PROFILE
|
||||
select HWMON
|
||||
|
@ -52,9 +52,7 @@ static void update_enumeration_value(int instance_id, char *attr_value)
|
||||
{
|
||||
struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
|
||||
|
||||
strscpy(enum_data->current_value,
|
||||
attr_value,
|
||||
sizeof(enum_data->current_value));
|
||||
strscpy(enum_data->current_value, attr_value);
|
||||
}
|
||||
|
||||
ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, enumeration);
|
||||
@ -174,8 +172,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
|
||||
case VALUE:
|
||||
break;
|
||||
case PATH:
|
||||
strscpy(enum_data->common.path, str_value,
|
||||
sizeof(enum_data->common.path));
|
||||
strscpy(enum_data->common.path, str_value);
|
||||
break;
|
||||
case IS_READONLY:
|
||||
enum_data->common.is_readonly = int_value;
|
||||
@ -222,9 +219,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
strscpy(enum_data->common.prerequisites[reqs],
|
||||
str_value,
|
||||
sizeof(enum_data->common.prerequisites[reqs]));
|
||||
strscpy(enum_data->common.prerequisites[reqs], str_value);
|
||||
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
@ -236,8 +231,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
|
||||
break;
|
||||
|
||||
case ENUM_CURRENT_VALUE:
|
||||
strscpy(enum_data->current_value,
|
||||
str_value, sizeof(enum_data->current_value));
|
||||
strscpy(enum_data->current_value, str_value);
|
||||
break;
|
||||
case ENUM_SIZE:
|
||||
if (int_value > MAX_VALUES_SIZE) {
|
||||
@ -278,9 +272,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum
|
||||
* is greater than MAX_VALUES_SIZE
|
||||
*/
|
||||
if (size < MAX_VALUES_SIZE)
|
||||
strscpy(enum_data->possible_values[pos_values],
|
||||
str_value,
|
||||
sizeof(enum_data->possible_values[pos_values]));
|
||||
strscpy(enum_data->possible_values[pos_values], str_value);
|
||||
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
|
@ -192,8 +192,7 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_
|
||||
integer_data->current_value = int_value;
|
||||
break;
|
||||
case PATH:
|
||||
strscpy(integer_data->common.path, str_value,
|
||||
sizeof(integer_data->common.path));
|
||||
strscpy(integer_data->common.path, str_value);
|
||||
break;
|
||||
case IS_READONLY:
|
||||
integer_data->common.is_readonly = int_value;
|
||||
@ -240,9 +239,7 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
strscpy(integer_data->common.prerequisites[reqs],
|
||||
str_value,
|
||||
sizeof(integer_data->common.prerequisites[reqs]));
|
||||
strscpy(integer_data->common.prerequisites[reqs], str_value);
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
}
|
||||
|
@ -57,9 +57,7 @@ static void update_ordered_list_value(int instance, char *attr_value)
|
||||
{
|
||||
struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance];
|
||||
|
||||
strscpy(ordered_list_data->current_value,
|
||||
attr_value,
|
||||
sizeof(ordered_list_data->current_value));
|
||||
strscpy(ordered_list_data->current_value, attr_value);
|
||||
}
|
||||
|
||||
ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, ordered_list);
|
||||
@ -179,13 +177,11 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
|
||||
/* Assign appropriate element value to corresponding field*/
|
||||
switch (eloc) {
|
||||
case VALUE:
|
||||
strscpy(ordered_list_data->current_value,
|
||||
str_value, sizeof(ordered_list_data->current_value));
|
||||
strscpy(ordered_list_data->current_value, str_value);
|
||||
replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP);
|
||||
break;
|
||||
case PATH:
|
||||
strscpy(ordered_list_data->common.path, str_value,
|
||||
sizeof(ordered_list_data->common.path));
|
||||
strscpy(ordered_list_data->common.path, str_value);
|
||||
break;
|
||||
case IS_READONLY:
|
||||
ordered_list_data->common.is_readonly = int_value;
|
||||
@ -227,9 +223,7 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
strscpy(ordered_list_data->common.prerequisites[reqs],
|
||||
str_value,
|
||||
sizeof(ordered_list_data->common.prerequisites[reqs]));
|
||||
strscpy(ordered_list_data->common.prerequisites[reqs], str_value);
|
||||
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
@ -271,9 +265,7 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord
|
||||
part = strsep(&part_tmp, COMMA_SEP);
|
||||
|
||||
for (olist_elem = 0; olist_elem < MAX_ELEMENTS_SIZE && part; olist_elem++) {
|
||||
strscpy(ordered_list_data->elements[olist_elem],
|
||||
part,
|
||||
sizeof(ordered_list_data->elements[olist_elem]));
|
||||
strscpy(ordered_list_data->elements[olist_elem], part);
|
||||
part = strsep(&part_tmp, COMMA_SEP);
|
||||
}
|
||||
ordered_list_data->elements_size = olist_elem;
|
||||
|
@ -101,13 +101,9 @@ static int store_password_instance(struct kobject *kobj, const char *buf,
|
||||
|
||||
if (!ret) {
|
||||
if (is_current)
|
||||
strscpy(bioscfg_drv.password_data[id].current_password,
|
||||
buf_cp,
|
||||
sizeof(bioscfg_drv.password_data[id].current_password));
|
||||
strscpy(bioscfg_drv.password_data[id].current_password, buf_cp);
|
||||
else
|
||||
strscpy(bioscfg_drv.password_data[id].new_password,
|
||||
buf_cp,
|
||||
sizeof(bioscfg_drv.password_data[id].new_password));
|
||||
strscpy(bioscfg_drv.password_data[id].new_password, buf_cp);
|
||||
}
|
||||
|
||||
kfree(buf_cp);
|
||||
@ -272,8 +268,7 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
|
||||
case VALUE:
|
||||
break;
|
||||
case PATH:
|
||||
strscpy(password_data->common.path, str_value,
|
||||
sizeof(password_data->common.path));
|
||||
strscpy(password_data->common.path, str_value);
|
||||
break;
|
||||
case IS_READONLY:
|
||||
password_data->common.is_readonly = int_value;
|
||||
@ -315,9 +310,7 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
strscpy(password_data->common.prerequisites[reqs],
|
||||
str_value,
|
||||
sizeof(password_data->common.prerequisites[reqs]));
|
||||
strscpy(password_data->common.prerequisites[reqs], str_value);
|
||||
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
@ -359,9 +352,7 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
strscpy(password_data->encodings[pos_values],
|
||||
str_value,
|
||||
sizeof(password_data->encodings[pos_values]));
|
||||
strscpy(password_data->encodings[pos_values], str_value);
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
|
||||
|
@ -365,8 +365,7 @@ int hp_populate_secure_platform_data(struct kobject *attr_name_kobj)
|
||||
/* Populate data for Secure Platform Management */
|
||||
bioscfg_drv.spm_data.attr_name_kobj = attr_name_kobj;
|
||||
|
||||
strscpy(bioscfg_drv.spm_data.attribute_name, SPM_STR,
|
||||
sizeof(bioscfg_drv.spm_data.attribute_name));
|
||||
strscpy(bioscfg_drv.spm_data.attribute_name, SPM_STR);
|
||||
|
||||
bioscfg_drv.spm_data.is_enabled = 0;
|
||||
bioscfg_drv.spm_data.mechanism = 0;
|
||||
|
@ -50,7 +50,7 @@ static void update_string_value(int instance_id, char *attr_value)
|
||||
struct string_data *string_data = &bioscfg_drv.string_data[instance_id];
|
||||
|
||||
/* Write settings to BIOS */
|
||||
strscpy(string_data->current_value, attr_value, sizeof(string_data->current_value));
|
||||
strscpy(string_data->current_value, attr_value);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -178,12 +178,10 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob
|
||||
/* Assign appropriate element value to corresponding field*/
|
||||
switch (eloc) {
|
||||
case VALUE:
|
||||
strscpy(string_data->current_value,
|
||||
str_value, sizeof(string_data->current_value));
|
||||
strscpy(string_data->current_value, str_value);
|
||||
break;
|
||||
case PATH:
|
||||
strscpy(string_data->common.path, str_value,
|
||||
sizeof(string_data->common.path));
|
||||
strscpy(string_data->common.path, str_value);
|
||||
break;
|
||||
case IS_READONLY:
|
||||
string_data->common.is_readonly = int_value;
|
||||
@ -231,9 +229,7 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
strscpy(string_data->common.prerequisites[reqs],
|
||||
str_value,
|
||||
sizeof(string_data->common.prerequisites[reqs]));
|
||||
strscpy(string_data->common.prerequisites[reqs], str_value);
|
||||
kfree(str_value);
|
||||
str_value = NULL;
|
||||
}
|
||||
|
@ -24,6 +24,9 @@
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dmi.h>
|
||||
@ -42,6 +45,8 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
|
||||
#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
|
||||
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
|
||||
|
||||
#define ACPI_AC_CLASS "ac_adapter"
|
||||
|
||||
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
|
||||
|
||||
/* DMI board names of devices that should use the omen specific path for
|
||||
@ -259,10 +264,18 @@ static const struct key_entry hp_wmi_keymap[] = {
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Mutex for the active_platform_profile variable,
|
||||
* see omen_powersource_event.
|
||||
*/
|
||||
static DEFINE_MUTEX(active_platform_profile_lock);
|
||||
|
||||
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 struct notifier_block platform_power_source_nb;
|
||||
static enum platform_profile_option active_platform_profile;
|
||||
static bool platform_profile_support;
|
||||
static bool zero_insize_support;
|
||||
|
||||
@ -1194,8 +1207,7 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
static int platform_profile_omen_get_ec(enum platform_profile_option *profile)
|
||||
{
|
||||
int tp;
|
||||
|
||||
@ -1223,6 +1235,27 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
/*
|
||||
* We directly return the stored platform profile, as the embedded
|
||||
* controller will not accept switching to the performance option when
|
||||
* the conditions are not met (e.g. the laptop is not plugged in).
|
||||
*
|
||||
* If we directly return what the EC reports, the platform profile will
|
||||
* immediately "switch back" to normal mode, which is against the
|
||||
* expected behaviour from a userspace point of view, as described in
|
||||
* the Platform Profile Section page of the kernel documentation.
|
||||
*
|
||||
* See also omen_powersource_event.
|
||||
*/
|
||||
guard(mutex)(&active_platform_profile_lock);
|
||||
*profile = active_platform_profile;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool has_omen_thermal_profile_ec_timer(void)
|
||||
{
|
||||
const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
|
||||
@ -1245,8 +1278,7 @@ inline int omen_thermal_profile_ec_timer_set(u8 value)
|
||||
return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
|
||||
}
|
||||
|
||||
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
static int platform_profile_omen_set_ec(enum platform_profile_option profile)
|
||||
{
|
||||
int err, tp, tp_version;
|
||||
enum hp_thermal_profile_omen_flags flags = 0;
|
||||
@ -1300,6 +1332,22 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err;
|
||||
|
||||
guard(mutex)(&active_platform_profile_lock);
|
||||
|
||||
err = platform_profile_omen_set_ec(profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
active_platform_profile = profile;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thermal_profile_get(void)
|
||||
{
|
||||
return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY);
|
||||
@ -1381,8 +1429,7 @@ static bool is_victus_thermal_profile(void)
|
||||
board_name) >= 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
static int platform_profile_victus_get_ec(enum platform_profile_option *profile)
|
||||
{
|
||||
int tp;
|
||||
|
||||
@ -1407,8 +1454,14 @@ static int platform_profile_victus_get(struct platform_profile_handler *pprof,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
static int platform_profile_victus_get(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option *profile)
|
||||
{
|
||||
/* Same behaviour as platform_profile_omen_get */
|
||||
return platform_profile_omen_get(pprof, profile);
|
||||
}
|
||||
|
||||
static int platform_profile_victus_set_ec(enum platform_profile_option profile)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
@ -1433,21 +1486,113 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int platform_profile_victus_set(struct platform_profile_handler *pprof,
|
||||
enum platform_profile_option profile)
|
||||
{
|
||||
int err;
|
||||
|
||||
guard(mutex)(&active_platform_profile_lock);
|
||||
|
||||
err = platform_profile_victus_set_ec(profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
active_platform_profile = profile;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omen_powersource_event(struct notifier_block *nb,
|
||||
unsigned long value,
|
||||
void *data)
|
||||
{
|
||||
struct acpi_bus_event *event_entry = data;
|
||||
enum platform_profile_option actual_profile;
|
||||
int err;
|
||||
|
||||
if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
pr_debug("Received power source device event\n");
|
||||
|
||||
guard(mutex)(&active_platform_profile_lock);
|
||||
|
||||
/*
|
||||
* This handler can only be called on Omen and Victus models, so
|
||||
* there's no need to call is_victus_thermal_profile() here.
|
||||
*/
|
||||
if (is_omen_thermal_profile())
|
||||
err = platform_profile_omen_get_ec(&actual_profile);
|
||||
else
|
||||
err = platform_profile_victus_get_ec(&actual_profile);
|
||||
|
||||
if (err < 0) {
|
||||
/*
|
||||
* Although we failed to get the current platform profile, we
|
||||
* still want the other event consumers to process it.
|
||||
*/
|
||||
pr_warn("Failed to read current platform profile (%d)\n", err);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're back on AC and that the user-chosen power profile is
|
||||
* different from what the EC reports, we restore the user-chosen
|
||||
* one.
|
||||
*/
|
||||
if (power_supply_is_system_supplied() <= 0 ||
|
||||
active_platform_profile == actual_profile) {
|
||||
pr_debug("Platform profile update skipped, conditions unmet\n");
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
if (is_omen_thermal_profile())
|
||||
err = platform_profile_omen_set_ec(active_platform_profile);
|
||||
else
|
||||
err = platform_profile_victus_set_ec(active_platform_profile);
|
||||
|
||||
if (err < 0) {
|
||||
pr_warn("Failed to restore platform profile (%d)\n", err);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int omen_register_powersource_event_handler(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
platform_power_source_nb.notifier_call = omen_powersource_event;
|
||||
err = register_acpi_notifier(&platform_power_source_nb);
|
||||
|
||||
if (err < 0) {
|
||||
pr_warn("Failed to install ACPI power source notify handler\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void omen_unregister_powersource_event_handler(void)
|
||||
{
|
||||
unregister_acpi_notifier(&platform_power_source_nb);
|
||||
}
|
||||
|
||||
static int thermal_profile_setup(void)
|
||||
{
|
||||
int err, tp;
|
||||
|
||||
if (is_omen_thermal_profile()) {
|
||||
tp = omen_thermal_profile_get();
|
||||
if (tp < 0)
|
||||
return tp;
|
||||
err = platform_profile_omen_get_ec(&active_platform_profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* call thermal profile write command to ensure that the
|
||||
* firmware correctly sets the OEM variables
|
||||
*/
|
||||
|
||||
err = omen_thermal_profile_set(tp);
|
||||
err = platform_profile_omen_set_ec(active_platform_profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -1456,15 +1601,15 @@ static int thermal_profile_setup(void)
|
||||
|
||||
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;
|
||||
err = platform_profile_victus_get_ec(&active_platform_profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* call thermal profile write command to ensure that the
|
||||
* firmware correctly sets the OEM variables
|
||||
*/
|
||||
err = omen_thermal_profile_set(tp);
|
||||
err = platform_profile_victus_set_ec(active_platform_profile);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -1758,6 +1903,12 @@ static int __init hp_wmi_init(void)
|
||||
goto err_unregister_device;
|
||||
}
|
||||
|
||||
if (is_omen_thermal_profile() || is_victus_thermal_profile()) {
|
||||
err = omen_register_powersource_event_handler();
|
||||
if (err)
|
||||
goto err_unregister_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_device:
|
||||
@ -1772,6 +1923,9 @@ module_init(hp_wmi_init);
|
||||
|
||||
static void __exit hp_wmi_exit(void)
|
||||
{
|
||||
if (is_omen_thermal_profile() || is_victus_thermal_profile())
|
||||
omen_unregister_powersource_event_handler();
|
||||
|
||||
if (wmi_has_guid(HPWMI_EVENT_GUID))
|
||||
hp_wmi_input_destroy();
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
@ -204,7 +205,7 @@ static int ideapad_shared_init(struct ideapad_private *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ideapad_shared_mutex);
|
||||
guard(mutex)(&ideapad_shared_mutex);
|
||||
|
||||
if (!ideapad_shared) {
|
||||
ideapad_shared = priv;
|
||||
@ -214,19 +215,15 @@ static int ideapad_shared_init(struct ideapad_private *priv)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&ideapad_shared_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ideapad_shared_exit(struct ideapad_private *priv)
|
||||
{
|
||||
mutex_lock(&ideapad_shared_mutex);
|
||||
guard(mutex)(&ideapad_shared_mutex);
|
||||
|
||||
if (ideapad_shared == priv)
|
||||
ideapad_shared = NULL;
|
||||
|
||||
mutex_unlock(&ideapad_shared_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -840,36 +837,33 @@ static int dytc_profile_set(struct platform_profile_handler *pprof,
|
||||
unsigned long output;
|
||||
int err;
|
||||
|
||||
err = mutex_lock_interruptible(&dytc->mutex);
|
||||
if (err)
|
||||
return err;
|
||||
scoped_guard(mutex_intr, &dytc->mutex) {
|
||||
if (profile == PLATFORM_PROFILE_BALANCED) {
|
||||
/* To get back to balanced mode we just issue a reset command */
|
||||
err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
int perfmode;
|
||||
|
||||
if (profile == PLATFORM_PROFILE_BALANCED) {
|
||||
/* To get back to balanced mode we just issue a reset command */
|
||||
err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL);
|
||||
if (err)
|
||||
goto unlock;
|
||||
} else {
|
||||
int perfmode;
|
||||
err = convert_profile_to_dytc(profile, &perfmode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = convert_profile_to_dytc(profile, &perfmode);
|
||||
if (err)
|
||||
goto unlock;
|
||||
/* Determine if we are in CQL mode. This alters the commands we do */
|
||||
err = dytc_cql_command(priv,
|
||||
DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1),
|
||||
&output);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Determine if we are in CQL mode. This alters the commands we do */
|
||||
err = dytc_cql_command(priv, DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1),
|
||||
&output);
|
||||
if (err)
|
||||
goto unlock;
|
||||
/* Success - update current profile */
|
||||
dytc->current_profile = profile;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Success - update current profile */
|
||||
dytc->current_profile = profile;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&dytc->mutex);
|
||||
|
||||
return err;
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
static void dytc_profile_refresh(struct ideapad_private *priv)
|
||||
@ -878,9 +872,8 @@ static void dytc_profile_refresh(struct ideapad_private *priv)
|
||||
unsigned long output;
|
||||
int err, perfmode;
|
||||
|
||||
mutex_lock(&priv->dytc->mutex);
|
||||
err = dytc_cql_command(priv, DYTC_CMD_GET, &output);
|
||||
mutex_unlock(&priv->dytc->mutex);
|
||||
scoped_guard(mutex, &priv->dytc->mutex)
|
||||
err = dytc_cql_command(priv, DYTC_CMD_GET, &output);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
@ -1809,11 +1802,11 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
|
||||
struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev);
|
||||
struct ideapad_private *priv;
|
||||
|
||||
mutex_lock(&ideapad_shared_mutex);
|
||||
guard(mutex)(&ideapad_shared_mutex);
|
||||
|
||||
priv = ideapad_shared;
|
||||
if (!priv)
|
||||
goto unlock;
|
||||
return;
|
||||
|
||||
switch (wpriv->event) {
|
||||
case IDEAPAD_WMI_EVENT_ESC:
|
||||
@ -1847,8 +1840,6 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
|
||||
|
||||
break;
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&ideapad_shared_mutex);
|
||||
}
|
||||
|
||||
static const struct ideapad_wmi_private ideapad_wmi_context_esc = {
|
||||
|
@ -192,10 +192,14 @@ config INTEL_SMARTCONNECT
|
||||
This driver checks to determine whether the device has Intel Smart
|
||||
Connect enabled, and if so disables it.
|
||||
|
||||
config INTEL_TPMI_POWER_DOMAINS
|
||||
tristate
|
||||
|
||||
config INTEL_TPMI
|
||||
tristate "Intel Topology Aware Register and PM Capsule Interface (TPMI)"
|
||||
depends on INTEL_VSEC
|
||||
depends on X86_64
|
||||
select INTEL_TPMI_POWER_DOMAINS
|
||||
help
|
||||
The Intel Topology Aware Register and PM Capsule Interface (TPMI),
|
||||
provides enumerable MMIO interface for power management features.
|
||||
@ -205,6 +209,13 @@ config INTEL_TPMI
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel_vsec_tpmi.
|
||||
|
||||
config INTEL_PLR_TPMI
|
||||
tristate "Intel SoC TPMI Power Limit Reasons driver"
|
||||
depends on INTEL_TPMI
|
||||
help
|
||||
This driver provides the TPMI power limit reasons status information
|
||||
via debugfs files.
|
||||
|
||||
config INTEL_TURBO_MAX_3
|
||||
bool "Intel Turbo Boost Max Technology 3.0 enumeration driver"
|
||||
depends on X86_64 && SCHED_MC_PRIO
|
||||
|
@ -52,6 +52,10 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
||||
# TPMI drivers
|
||||
intel_vsec_tpmi-y := tpmi.o
|
||||
obj-$(CONFIG_INTEL_TPMI) += intel_vsec_tpmi.o
|
||||
obj-$(CONFIG_INTEL_PLR_TPMI) += intel_plr_tpmi.o
|
||||
|
||||
intel_tpmi_power_domains-y := tpmi_power_domains.o
|
||||
obj-$(CONFIG_INTEL_TPMI_POWER_DOMAINS) += intel_tpmi_power_domains.o
|
||||
|
||||
# Intel Uncore drivers
|
||||
intel-rst-y := rst.o
|
||||
|
@ -270,7 +270,7 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
|
||||
}
|
||||
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strscpy(board_info.type, "max17047", I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, "max17047");
|
||||
board_info.dev_name = "max17047";
|
||||
board_info.fwnode = fwnode;
|
||||
data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info);
|
||||
@ -361,7 +361,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strscpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, "typec_fusb302");
|
||||
board_info.dev_name = "fusb302";
|
||||
board_info.fwnode = fwnode;
|
||||
board_info.irq = fusb302_irq;
|
||||
@ -381,7 +381,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev)
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
board_info.dev_name = "pi3usb30532";
|
||||
board_info.fwnode = fwnode;
|
||||
strscpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, "pi3usb30532");
|
||||
|
||||
data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
|
||||
if (IS_ERR(data->pi3usb30532)) {
|
||||
|
@ -11,16 +11,15 @@
|
||||
|
||||
#include "ifs.h"
|
||||
|
||||
#define X86_MATCH(model, array_gen) \
|
||||
X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, \
|
||||
INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, array_gen)
|
||||
#define X86_MATCH(vfm, array_gen) \
|
||||
X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_CORE_CAPABILITIES, array_gen)
|
||||
|
||||
static const struct x86_cpu_id ifs_cpu_ids[] __initconst = {
|
||||
X86_MATCH(SAPPHIRERAPIDS_X, ARRAY_GEN0),
|
||||
X86_MATCH(EMERALDRAPIDS_X, ARRAY_GEN0),
|
||||
X86_MATCH(GRANITERAPIDS_X, ARRAY_GEN0),
|
||||
X86_MATCH(GRANITERAPIDS_D, ARRAY_GEN0),
|
||||
X86_MATCH(ATOM_CRESTMONT_X, ARRAY_GEN1),
|
||||
X86_MATCH(INTEL_SAPPHIRERAPIDS_X, ARRAY_GEN0),
|
||||
X86_MATCH(INTEL_EMERALDRAPIDS_X, ARRAY_GEN0),
|
||||
X86_MATCH(INTEL_GRANITERAPIDS_X, ARRAY_GEN0),
|
||||
X86_MATCH(INTEL_GRANITERAPIDS_D, ARRAY_GEN0),
|
||||
X86_MATCH(INTEL_ATOM_CRESTMONT_X, ARRAY_GEN1),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids);
|
||||
|
354
drivers/platform/x86/intel/intel_plr_tpmi.c
Normal file
354
drivers/platform/x86/intel/intel_plr_tpmi.c
Normal file
@ -0,0 +1,354 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Performance Limit Reasons via TPMI
|
||||
*
|
||||
* Copyright (c) 2024, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/intel_tpmi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "tpmi_power_domains.h"
|
||||
|
||||
#define PLR_HEADER 0x00
|
||||
#define PLR_MAILBOX_INTERFACE 0x08
|
||||
#define PLR_MAILBOX_DATA 0x10
|
||||
#define PLR_DIE_LEVEL 0x18
|
||||
|
||||
#define PLR_MODULE_ID_MASK GENMASK_ULL(19, 12)
|
||||
#define PLR_RUN_BUSY BIT_ULL(63)
|
||||
|
||||
#define PLR_COMMAND_WRITE 1
|
||||
|
||||
#define PLR_INVALID GENMASK_ULL(63, 0)
|
||||
|
||||
#define PLR_TIMEOUT_US 5
|
||||
#define PLR_TIMEOUT_MAX_US 1000
|
||||
|
||||
#define PLR_COARSE_REASON_BITS 32
|
||||
|
||||
struct tpmi_plr;
|
||||
|
||||
struct tpmi_plr_die {
|
||||
void __iomem *base;
|
||||
struct mutex lock; /* Protect access to PLR mailbox */
|
||||
int package_id;
|
||||
int die_id;
|
||||
struct tpmi_plr *plr;
|
||||
};
|
||||
|
||||
struct tpmi_plr {
|
||||
struct dentry *dbgfs_dir;
|
||||
struct tpmi_plr_die *die_info;
|
||||
int num_dies;
|
||||
struct auxiliary_device *auxdev;
|
||||
};
|
||||
|
||||
static const char * const plr_coarse_reasons[] = {
|
||||
"FREQUENCY",
|
||||
"CURRENT",
|
||||
"POWER",
|
||||
"THERMAL",
|
||||
"PLATFORM",
|
||||
"MCP",
|
||||
"RAS",
|
||||
"MISC",
|
||||
"QOS",
|
||||
"DFC",
|
||||
};
|
||||
|
||||
static const char * const plr_fine_reasons[] = {
|
||||
"FREQUENCY_CDYN0",
|
||||
"FREQUENCY_CDYN1",
|
||||
"FREQUENCY_CDYN2",
|
||||
"FREQUENCY_CDYN3",
|
||||
"FREQUENCY_CDYN4",
|
||||
"FREQUENCY_CDYN5",
|
||||
"FREQUENCY_FCT",
|
||||
"FREQUENCY_PCS_TRL",
|
||||
"CURRENT_MTPMAX",
|
||||
"POWER_FAST_RAPL",
|
||||
"POWER_PKG_PL1_MSR_TPMI",
|
||||
"POWER_PKG_PL1_MMIO",
|
||||
"POWER_PKG_PL1_PCS",
|
||||
"POWER_PKG_PL2_MSR_TPMI",
|
||||
"POWER_PKG_PL2_MMIO",
|
||||
"POWER_PKG_PL2_PCS",
|
||||
"POWER_PLATFORM_PL1_MSR_TPMI",
|
||||
"POWER_PLATFORM_PL1_MMIO",
|
||||
"POWER_PLATFORM_PL1_PCS",
|
||||
"POWER_PLATFORM_PL2_MSR_TPMI",
|
||||
"POWER_PLATFORM_PL2_MMIO",
|
||||
"POWER_PLATFORM_PL2_PCS",
|
||||
"UNKNOWN(22)",
|
||||
"THERMAL_PER_CORE",
|
||||
"DFC_UFS",
|
||||
"PLATFORM_PROCHOT",
|
||||
"PLATFORM_HOT_VR",
|
||||
"UNKNOWN(27)",
|
||||
"UNKNOWN(28)",
|
||||
"MISC_PCS_PSTATE",
|
||||
};
|
||||
|
||||
static u64 plr_read(struct tpmi_plr_die *plr_die, int offset)
|
||||
{
|
||||
return readq(plr_die->base + offset);
|
||||
}
|
||||
|
||||
static void plr_write(u64 val, struct tpmi_plr_die *plr_die, int offset)
|
||||
{
|
||||
writeq(val, plr_die->base + offset);
|
||||
}
|
||||
|
||||
static int plr_read_cpu_status(struct tpmi_plr_die *plr_die, int cpu,
|
||||
u64 *status)
|
||||
{
|
||||
u64 regval;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&plr_die->lock);
|
||||
|
||||
regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu));
|
||||
regval |= PLR_RUN_BUSY;
|
||||
|
||||
plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE);
|
||||
|
||||
ret = readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval,
|
||||
!(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US,
|
||||
PLR_TIMEOUT_MAX_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*status = plr_read(plr_die, PLR_MAILBOX_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plr_clear_cpu_status(struct tpmi_plr_die *plr_die, int cpu)
|
||||
{
|
||||
u64 regval;
|
||||
|
||||
lockdep_assert_held(&plr_die->lock);
|
||||
|
||||
regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu));
|
||||
regval |= PLR_RUN_BUSY | PLR_COMMAND_WRITE;
|
||||
|
||||
plr_write(0, plr_die, PLR_MAILBOX_DATA);
|
||||
|
||||
plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE);
|
||||
|
||||
return readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval,
|
||||
!(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US,
|
||||
PLR_TIMEOUT_MAX_US);
|
||||
}
|
||||
|
||||
static void plr_print_bits(struct seq_file *s, u64 val, int bits)
|
||||
{
|
||||
const unsigned long mask[] = { BITMAP_FROM_U64(val) };
|
||||
int bit, index;
|
||||
|
||||
for_each_set_bit(bit, mask, bits) {
|
||||
const char *str = NULL;
|
||||
|
||||
if (bit < PLR_COARSE_REASON_BITS) {
|
||||
if (bit < ARRAY_SIZE(plr_coarse_reasons))
|
||||
str = plr_coarse_reasons[bit];
|
||||
} else {
|
||||
index = bit - PLR_COARSE_REASON_BITS;
|
||||
if (index < ARRAY_SIZE(plr_fine_reasons))
|
||||
str = plr_fine_reasons[index];
|
||||
}
|
||||
|
||||
if (str)
|
||||
seq_printf(s, " %s", str);
|
||||
else
|
||||
seq_printf(s, " UNKNOWN(%d)", bit);
|
||||
}
|
||||
|
||||
if (!val)
|
||||
seq_puts(s, " none");
|
||||
|
||||
seq_putc(s, '\n');
|
||||
}
|
||||
|
||||
static int plr_status_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct tpmi_plr_die *plr_die = s->private;
|
||||
int ret;
|
||||
u64 val;
|
||||
|
||||
val = plr_read(plr_die, PLR_DIE_LEVEL);
|
||||
seq_puts(s, "cpus");
|
||||
plr_print_bits(s, val, 32);
|
||||
|
||||
guard(mutex)(&plr_die->lock);
|
||||
|
||||
for (int cpu = 0; cpu < nr_cpu_ids; cpu++) {
|
||||
if (plr_die->die_id != tpmi_get_power_domain_id(cpu))
|
||||
continue;
|
||||
|
||||
if (plr_die->package_id != topology_physical_package_id(cpu))
|
||||
continue;
|
||||
|
||||
seq_printf(s, "cpu%d", cpu);
|
||||
ret = plr_read_cpu_status(plr_die, cpu, &val);
|
||||
if (ret) {
|
||||
dev_err(&plr_die->plr->auxdev->dev, "Failed to read PLR for cpu %d, ret=%d\n",
|
||||
cpu, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
plr_print_bits(s, val, 64);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t plr_status_write(struct file *filp, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = filp->private_data;
|
||||
struct tpmi_plr_die *plr_die = s->private;
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool_from_user(ubuf, count, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
plr_write(0, plr_die, PLR_DIE_LEVEL);
|
||||
|
||||
guard(mutex)(&plr_die->lock);
|
||||
|
||||
for (int cpu = 0; cpu < nr_cpu_ids; cpu++) {
|
||||
if (plr_die->die_id != tpmi_get_power_domain_id(cpu))
|
||||
continue;
|
||||
|
||||
if (plr_die->package_id != topology_physical_package_id(cpu))
|
||||
continue;
|
||||
|
||||
plr_clear_cpu_status(plr_die, cpu);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(plr_status);
|
||||
|
||||
static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct intel_tpmi_plat_info *plat_info;
|
||||
struct dentry *dentry;
|
||||
int i, num_resources;
|
||||
struct resource *res;
|
||||
struct tpmi_plr *plr;
|
||||
void __iomem *base;
|
||||
char name[16];
|
||||
int err;
|
||||
|
||||
plat_info = tpmi_get_platform_data(auxdev);
|
||||
if (!plat_info)
|
||||
return dev_err_probe(&auxdev->dev, -EINVAL, "No platform info\n");
|
||||
|
||||
dentry = tpmi_get_debugfs_dir(auxdev);
|
||||
if (!dentry)
|
||||
return dev_err_probe(&auxdev->dev, -ENODEV, "No TPMI debugfs directory.\n");
|
||||
|
||||
num_resources = tpmi_get_resource_count(auxdev);
|
||||
if (!num_resources)
|
||||
return -EINVAL;
|
||||
|
||||
plr = devm_kzalloc(&auxdev->dev, sizeof(*plr), GFP_KERNEL);
|
||||
if (!plr)
|
||||
return -ENOMEM;
|
||||
|
||||
plr->die_info = devm_kcalloc(&auxdev->dev, num_resources, sizeof(*plr->die_info),
|
||||
GFP_KERNEL);
|
||||
if (!plr->die_info)
|
||||
return -ENOMEM;
|
||||
|
||||
plr->num_dies = num_resources;
|
||||
plr->dbgfs_dir = debugfs_create_dir("plr", dentry);
|
||||
plr->auxdev = auxdev;
|
||||
|
||||
for (i = 0; i < num_resources; i++) {
|
||||
res = tpmi_get_resource_at_index(auxdev, i);
|
||||
if (!res) {
|
||||
err = dev_err_probe(&auxdev->dev, -EINVAL, "No resource\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
base = devm_ioremap_resource(&auxdev->dev, res);
|
||||
if (IS_ERR(base)) {
|
||||
err = PTR_ERR(base);
|
||||
goto err;
|
||||
}
|
||||
|
||||
plr->die_info[i].base = base;
|
||||
plr->die_info[i].package_id = plat_info->package_id;
|
||||
plr->die_info[i].die_id = i;
|
||||
plr->die_info[i].plr = plr;
|
||||
mutex_init(&plr->die_info[i].lock);
|
||||
|
||||
if (plr_read(&plr->die_info[i], PLR_HEADER) == PLR_INVALID)
|
||||
continue;
|
||||
|
||||
snprintf(name, sizeof(name), "domain%d", i);
|
||||
|
||||
dentry = debugfs_create_dir(name, plr->dbgfs_dir);
|
||||
debugfs_create_file("status", 0444, dentry, &plr->die_info[i],
|
||||
&plr_status_fops);
|
||||
}
|
||||
|
||||
auxiliary_set_drvdata(auxdev, plr);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
debugfs_remove_recursive(plr->dbgfs_dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void intel_plr_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct tpmi_plr *plr = auxiliary_get_drvdata(auxdev);
|
||||
|
||||
debugfs_remove_recursive(plr->dbgfs_dir);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id intel_plr_id_table[] = {
|
||||
{ .name = "intel_vsec.tpmi-plr" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, intel_plr_id_table);
|
||||
|
||||
static struct auxiliary_driver intel_plr_aux_driver = {
|
||||
.id_table = intel_plr_id_table,
|
||||
.remove = intel_plr_remove,
|
||||
.probe = intel_plr_probe,
|
||||
};
|
||||
module_auxiliary_driver(intel_plr_aux_driver);
|
||||
|
||||
MODULE_IMPORT_NS(INTEL_TPMI);
|
||||
MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN);
|
||||
MODULE_DESCRIPTION("Intel TPMI PLR Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -87,35 +87,26 @@ static int set_etr3(struct pmc_dev *pmcdev)
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
const struct pmc_reg_map *map = pmc->map;
|
||||
u32 reg;
|
||||
int err;
|
||||
|
||||
if (!map->etr3_offset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
/* check if CF9 is locked */
|
||||
reg = pmc_core_reg_read(pmc, map->etr3_offset);
|
||||
if (reg & ETR3_CF9LOCK) {
|
||||
err = -EACCES;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (reg & ETR3_CF9LOCK)
|
||||
return -EACCES;
|
||||
|
||||
/* write CF9 global reset bit */
|
||||
reg |= ETR3_CF9GR;
|
||||
pmc_core_reg_write(pmc, map->etr3_offset, reg);
|
||||
|
||||
reg = pmc_core_reg_read(pmc, map->etr3_offset);
|
||||
if (!(reg & ETR3_CF9GR)) {
|
||||
err = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!(reg & ETR3_CF9GR))
|
||||
return -EIO;
|
||||
|
||||
err = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
static umode_t etr3_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
@ -127,9 +118,8 @@ static umode_t etr3_is_visible(struct kobject *kobj,
|
||||
const struct pmc_reg_map *map = pmc->map;
|
||||
u32 reg;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
reg = pmc_core_reg_read(pmc, map->etr3_offset);
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
scoped_guard(mutex, &pmcdev->lock)
|
||||
reg = pmc_core_reg_read(pmc, map->etr3_offset);
|
||||
|
||||
return reg & ETR3_CF9LOCK ? attr->mode & (SYSFS_PREALLOC | 0444) : attr->mode;
|
||||
}
|
||||
@ -145,12 +135,10 @@ static ssize_t etr3_show(struct device *dev,
|
||||
if (!map->etr3_offset)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmc, map->etr3_offset);
|
||||
reg &= ETR3_CF9GR | ETR3_CF9LOCK;
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
scoped_guard(mutex, &pmcdev->lock) {
|
||||
reg = pmc_core_reg_read(pmc, map->etr3_offset);
|
||||
reg &= ETR3_CF9GR | ETR3_CF9LOCK;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "0x%08x", reg);
|
||||
}
|
||||
@ -257,9 +245,9 @@ static void pmc_core_slps0_display(struct pmc *pmc, struct device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
static int pmc_core_lpm_get_arr_size(const struct pmc_bit_map **maps)
|
||||
static unsigned int pmc_core_lpm_get_arr_size(const struct pmc_bit_map **maps)
|
||||
{
|
||||
int idx;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; maps[idx]; idx++)
|
||||
;/* Nothing */
|
||||
@ -272,8 +260,8 @@ static void pmc_core_lpm_display(struct pmc *pmc, struct device *dev,
|
||||
const char *str,
|
||||
const struct pmc_bit_map **maps)
|
||||
{
|
||||
int index, idx, len = 32, bit_mask, arr_size;
|
||||
u32 *lpm_regs;
|
||||
unsigned int index, idx, len = 32, arr_size;
|
||||
u32 bit_mask, *lpm_regs;
|
||||
|
||||
arr_size = pmc_core_lpm_get_arr_size(maps);
|
||||
lpm_regs = kmalloc_array(arr_size, sizeof(*lpm_regs), GFP_KERNEL);
|
||||
@ -326,13 +314,13 @@ static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip,
|
||||
static int pmc_core_ppfear_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
|
||||
struct pmc *pmc = pmcdev->pmcs[i];
|
||||
const struct pmc_bit_map **maps;
|
||||
u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES];
|
||||
int index, iter, idx, ip = 0;
|
||||
unsigned int index, iter, idx, ip = 0;
|
||||
|
||||
if (!pmc)
|
||||
continue;
|
||||
@ -391,7 +379,8 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused)
|
||||
const struct pmc_bit_map *map = pmc->map->mphy_sts;
|
||||
u32 mphy_core_reg_low, mphy_core_reg_high;
|
||||
u32 val_low, val_high;
|
||||
int index, err = 0;
|
||||
unsigned int index;
|
||||
int err = 0;
|
||||
|
||||
if (pmcdev->pmc_xram_read_bit) {
|
||||
seq_puts(s, "Access denied: please disable PMC_READ_DISABLE setting in BIOS.");
|
||||
@ -401,20 +390,18 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused)
|
||||
mphy_core_reg_low = (SPT_PMC_MPHY_CORE_STS_0 << 16);
|
||||
mphy_core_reg_high = (SPT_PMC_MPHY_CORE_STS_1 << 16);
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
if (pmc_core_send_msg(pmc, &mphy_core_reg_low) != 0) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
err = pmc_core_send_msg(pmc, &mphy_core_reg_low);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
msleep(10);
|
||||
val_low = pmc_core_reg_read(pmc, SPT_PMC_MFPMC_OFFSET);
|
||||
|
||||
if (pmc_core_send_msg(pmc, &mphy_core_reg_high) != 0) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
err = pmc_core_send_msg(pmc, &mphy_core_reg_high);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
msleep(10);
|
||||
val_high = pmc_core_reg_read(pmc, SPT_PMC_MFPMC_OFFSET);
|
||||
@ -433,9 +420,7 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused)
|
||||
"Power gated");
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_mphy_pg);
|
||||
|
||||
@ -445,7 +430,8 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused)
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
const struct pmc_bit_map *map = pmc->map->pll_sts;
|
||||
u32 mphy_common_reg, val;
|
||||
int index, err = 0;
|
||||
unsigned int index;
|
||||
int err = 0;
|
||||
|
||||
if (pmcdev->pmc_xram_read_bit) {
|
||||
seq_puts(s, "Access denied: please disable PMC_READ_DISABLE setting in BIOS.");
|
||||
@ -453,12 +439,11 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused)
|
||||
}
|
||||
|
||||
mphy_common_reg = (SPT_PMC_MPHY_COM_STS_0 << 16);
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
if (pmc_core_send_msg(pmc, &mphy_common_reg) != 0) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
err = pmc_core_send_msg(pmc, &mphy_common_reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Observed PMC HW response latency for MTPMC-MFPMC is ~10 ms */
|
||||
msleep(10);
|
||||
@ -470,9 +455,7 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused)
|
||||
map[index].bit_mask & val ? "Active" : "Idle");
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_pll);
|
||||
|
||||
@ -481,7 +464,8 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore)
|
||||
struct pmc *pmc;
|
||||
const struct pmc_reg_map *map;
|
||||
u32 reg;
|
||||
int pmc_index, ltr_index;
|
||||
unsigned int pmc_index;
|
||||
int ltr_index;
|
||||
|
||||
ltr_index = value;
|
||||
/* For platforms with multiple pmcs, ltr index value given by user
|
||||
@ -511,7 +495,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore)
|
||||
|
||||
pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index);
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmc, map->ltr_ignore_offset);
|
||||
if (ignore)
|
||||
@ -520,48 +504,56 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore)
|
||||
reg &= ~BIT(ltr_index);
|
||||
pmc_core_reg_write(pmc, map->ltr_ignore_offset, reg);
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t pmc_core_ltr_write(struct pmc_dev *pmcdev,
|
||||
const char __user *userbuf,
|
||||
size_t count, int ignore)
|
||||
{
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
err = kstrtou32_from_user(userbuf, count, 10, &value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pmc_core_send_ltr_ignore(pmcdev, value, ignore);
|
||||
|
||||
return err ?: count;
|
||||
}
|
||||
|
||||
static ssize_t pmc_core_ltr_ignore_write(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
u32 buf_size, value;
|
||||
int err;
|
||||
|
||||
buf_size = min_t(u32, count, 64);
|
||||
|
||||
err = kstrtou32_from_user(userbuf, buf_size, 10, &value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pmc_core_send_ltr_ignore(pmcdev, value, 1);
|
||||
|
||||
return err == 0 ? count : err;
|
||||
return pmc_core_ltr_write(pmcdev, userbuf, count, 1);
|
||||
}
|
||||
|
||||
static int pmc_core_ltr_ignore_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(pmc_core_ltr_ignore);
|
||||
|
||||
static int pmc_core_ltr_ignore_open(struct inode *inode, struct file *file)
|
||||
static ssize_t pmc_core_ltr_restore_write(struct file *file,
|
||||
const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return single_open(file, pmc_core_ltr_ignore_show, inode->i_private);
|
||||
struct seq_file *s = file->private_data;
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
|
||||
return pmc_core_ltr_write(pmcdev, userbuf, count, 0);
|
||||
}
|
||||
|
||||
static const struct file_operations pmc_core_ltr_ignore_ops = {
|
||||
.open = pmc_core_ltr_ignore_open,
|
||||
.read = seq_read,
|
||||
.write = pmc_core_ltr_ignore_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
static int pmc_core_ltr_restore_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(pmc_core_ltr_restore);
|
||||
|
||||
static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset)
|
||||
{
|
||||
@ -569,10 +561,10 @@ static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset)
|
||||
const struct pmc_reg_map *map = pmc->map;
|
||||
u32 fd;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
if (!reset && !slps0_dbg_latch)
|
||||
goto out_unlock;
|
||||
return;
|
||||
|
||||
fd = pmc_core_reg_read(pmc, map->slps0_dbg_offset);
|
||||
if (reset)
|
||||
@ -582,9 +574,6 @@ static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset)
|
||||
pmc_core_reg_write(pmc, map->slps0_dbg_offset, fd);
|
||||
|
||||
slps0_dbg_latch = false;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
}
|
||||
|
||||
static int pmc_core_slps0_dbg_show(struct seq_file *s, void *unused)
|
||||
@ -639,17 +628,29 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
|
||||
u64 decoded_snoop_ltr, decoded_non_snoop_ltr;
|
||||
u32 ltr_raw_data, scale, val;
|
||||
u16 snoop_ltr, nonsnoop_ltr;
|
||||
int i, index, ltr_index = 0;
|
||||
unsigned int i, index, ltr_index = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
|
||||
struct pmc *pmc = pmcdev->pmcs[i];
|
||||
struct pmc *pmc;
|
||||
const struct pmc_bit_map *map;
|
||||
u32 ltr_ign_reg;
|
||||
|
||||
pmc = pmcdev->pmcs[i];
|
||||
if (!pmc)
|
||||
continue;
|
||||
|
||||
scoped_guard(mutex, &pmcdev->lock)
|
||||
ltr_ign_reg = pmc_core_reg_read(pmc, pmc->map->ltr_ignore_offset);
|
||||
|
||||
map = pmc->map->ltr_show_sts;
|
||||
for (index = 0; map[index].name; index++) {
|
||||
bool ltr_ign_data;
|
||||
|
||||
if (index > pmc->map->ltr_ignore_max)
|
||||
ltr_ign_data = false;
|
||||
else
|
||||
ltr_ign_data = ltr_ign_reg & BIT(index);
|
||||
|
||||
decoded_snoop_ltr = decoded_non_snoop_ltr = 0;
|
||||
ltr_raw_data = pmc_core_reg_read(pmc,
|
||||
map[index].bit_mask);
|
||||
@ -667,10 +668,10 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
|
||||
decoded_snoop_ltr = val * convert_ltr_scale(scale);
|
||||
}
|
||||
|
||||
seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\n",
|
||||
seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n",
|
||||
ltr_index, i, map[index].name, ltr_raw_data,
|
||||
decoded_non_snoop_ltr,
|
||||
decoded_snoop_ltr);
|
||||
decoded_snoop_ltr, ltr_ign_data);
|
||||
ltr_index++;
|
||||
}
|
||||
}
|
||||
@ -727,7 +728,8 @@ static int pmc_core_substate_res_show(struct seq_file *s, void *unused)
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2;
|
||||
u32 offset = pmc->map->lpm_residency_offset;
|
||||
int i, mode;
|
||||
unsigned int i;
|
||||
int mode;
|
||||
|
||||
seq_printf(s, "%-10s %-15s\n", "Substate", "Residency");
|
||||
|
||||
@ -743,7 +745,7 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_res);
|
||||
static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
|
||||
struct pmc *pmc = pmcdev->pmcs[i];
|
||||
@ -764,7 +766,7 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_sts_regs);
|
||||
static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
|
||||
struct pmc *pmc = pmcdev->pmcs[i];
|
||||
@ -785,7 +787,8 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs);
|
||||
static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
int i, mode;
|
||||
unsigned int i;
|
||||
int mode;
|
||||
|
||||
seq_printf(s, "%30s |", "Element");
|
||||
pmc_for_each_mode(i, mode, pmcdev)
|
||||
@ -799,7 +802,8 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused)
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
u32 sts_offset;
|
||||
u32 *lpm_req_regs;
|
||||
int num_maps, mp, pmc_index;
|
||||
unsigned int mp, pmc_index;
|
||||
int num_maps;
|
||||
|
||||
for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) {
|
||||
struct pmc *pmc = pmcdev->pmcs[pmc_index];
|
||||
@ -921,9 +925,10 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
unsigned int idx;
|
||||
bool c10;
|
||||
u32 reg;
|
||||
int idx, mode;
|
||||
int mode;
|
||||
|
||||
reg = pmc_core_reg_read(pmc, pmc->map->lpm_sts_latch_en_offset);
|
||||
if (reg & LPM_STS_LATCH_MODE) {
|
||||
@ -955,7 +960,8 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
bool clear = false, c10 = false;
|
||||
unsigned char buf[8];
|
||||
int idx, m, mode;
|
||||
unsigned int idx;
|
||||
int m, mode;
|
||||
u32 reg;
|
||||
|
||||
if (count > sizeof(buf) - 1)
|
||||
@ -987,26 +993,22 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmc, pmc->map->etr3_offset);
|
||||
reg |= ETR3_CLEAR_LPM_EVENTS;
|
||||
pmc_core_reg_write(pmc, pmc->map->etr3_offset, reg);
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
if (c10) {
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
|
||||
reg = pmc_core_reg_read(pmc, pmc->map->lpm_sts_latch_en_offset);
|
||||
reg &= ~LPM_STS_LATCH_MODE;
|
||||
pmc_core_reg_write(pmc, pmc->map->lpm_sts_latch_en_offset, reg);
|
||||
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -1015,9 +1017,8 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file,
|
||||
* and clear everything else.
|
||||
*/
|
||||
reg = LPM_STS_LATCH_MODE | BIT(mode);
|
||||
mutex_lock(&pmcdev->lock);
|
||||
guard(mutex)(&pmcdev->lock);
|
||||
pmc_core_reg_write(pmc, pmc->map->lpm_sts_latch_en_offset, reg);
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -1028,7 +1029,7 @@ static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
|
||||
struct pmc *pmc = s->private;
|
||||
const struct pmc_bit_map *map = pmc->map->msr_sts;
|
||||
u64 pcstate_count;
|
||||
int index;
|
||||
unsigned int index;
|
||||
|
||||
for (index = 0; map[index].name ; index++) {
|
||||
if (rdmsrl_safe(map[index].bit_mask, &pcstate_count))
|
||||
@ -1046,7 +1047,7 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc);
|
||||
|
||||
static bool pmc_core_pri_verify(u32 lpm_pri, u8 *mode_order)
|
||||
{
|
||||
int i, j;
|
||||
unsigned int i, j;
|
||||
|
||||
if (!lpm_pri)
|
||||
return false;
|
||||
@ -1081,7 +1082,8 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev)
|
||||
u8 mode_order[LPM_MAX_NUM_MODES];
|
||||
u32 lpm_pri;
|
||||
u32 lpm_en;
|
||||
int mode, i, p;
|
||||
unsigned int i;
|
||||
int mode, p;
|
||||
|
||||
/* Use LPM Maps to indicate support for substates */
|
||||
if (!pmc->map->lpm_num_maps)
|
||||
@ -1228,7 +1230,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
||||
pmcdev, &pmc_core_ppfear_fops);
|
||||
|
||||
debugfs_create_file("ltr_ignore", 0644, dir, pmcdev,
|
||||
&pmc_core_ltr_ignore_ops);
|
||||
&pmc_core_ltr_ignore_fops);
|
||||
|
||||
debugfs_create_file("ltr_restore", 0200, dir, pmcdev, &pmc_core_ltr_restore_fops);
|
||||
|
||||
debugfs_create_file("ltr_show", 0444, dir, pmcdev, &pmc_core_ltr_fops);
|
||||
|
||||
@ -1293,29 +1297,29 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
||||
}
|
||||
|
||||
static const struct x86_cpu_id intel_pmc_core_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, spt_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, spt_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, spt_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, spt_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, cnp_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, icl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_NNPI, icl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, cnp_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, cnp_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, tgl_l_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, tgl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, tgl_l_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, icl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, tgl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, tgl_l_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, tgl_l_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, adl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, tgl_l_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, adl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, mtl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, arl_core_init),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, lnl_core_init),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE_L, spt_core_init),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE, spt_core_init),
|
||||
X86_MATCH_VFM(INTEL_KABYLAKE_L, spt_core_init),
|
||||
X86_MATCH_VFM(INTEL_KABYLAKE, spt_core_init),
|
||||
X86_MATCH_VFM(INTEL_CANNONLAKE_L, cnp_core_init),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_L, icl_core_init),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_NNPI, icl_core_init),
|
||||
X86_MATCH_VFM(INTEL_COMETLAKE, cnp_core_init),
|
||||
X86_MATCH_VFM(INTEL_COMETLAKE_L, cnp_core_init),
|
||||
X86_MATCH_VFM(INTEL_TIGERLAKE_L, tgl_l_core_init),
|
||||
X86_MATCH_VFM(INTEL_TIGERLAKE, tgl_core_init),
|
||||
X86_MATCH_VFM(INTEL_ATOM_TREMONT, tgl_l_core_init),
|
||||
X86_MATCH_VFM(INTEL_ATOM_TREMONT_L, icl_core_init),
|
||||
X86_MATCH_VFM(INTEL_ROCKETLAKE, tgl_core_init),
|
||||
X86_MATCH_VFM(INTEL_ALDERLAKE_L, tgl_l_core_init),
|
||||
X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, tgl_l_core_init),
|
||||
X86_MATCH_VFM(INTEL_ALDERLAKE, adl_core_init),
|
||||
X86_MATCH_VFM(INTEL_RAPTORLAKE_P, tgl_l_core_init),
|
||||
X86_MATCH_VFM(INTEL_RAPTORLAKE, adl_core_init),
|
||||
X86_MATCH_VFM(INTEL_RAPTORLAKE_S, adl_core_init),
|
||||
X86_MATCH_VFM(INTEL_METEORLAKE_L, mtl_core_init),
|
||||
X86_MATCH_VFM(INTEL_ARROWLAKE, arl_core_init),
|
||||
X86_MATCH_VFM(INTEL_LUNARLAKE_M, lnl_core_init),
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1373,7 +1377,7 @@ static void pmc_core_do_dmi_quirks(struct pmc *pmc)
|
||||
static void pmc_core_clean_structure(struct platform_device *pdev)
|
||||
{
|
||||
struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
|
||||
struct pmc *pmc = pmcdev->pmcs[i];
|
||||
@ -1536,7 +1540,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev)
|
||||
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
|
||||
const struct pmc_bit_map **maps = pmc->map->lpm_sts;
|
||||
int offset = pmc->map->lpm_status_offset;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
/* Check if the syspend used S0ix */
|
||||
if (pm_suspend_via_firmware())
|
||||
|
@ -35,14 +35,14 @@ static struct platform_device *pmc_core_device;
|
||||
* other list may grow, but this list should not.
|
||||
*/
|
||||
static const struct x86_cpu_id intel_pmc_core_platform_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &pmc_core_device),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_KABYLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_KABYLAKE, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_CANNONLAKE_L, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_L, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_COMETLAKE, &pmc_core_device),
|
||||
X86_MATCH_VFM(INTEL_COMETLAKE_L, &pmc_core_device),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_platform_ids);
|
||||
|
@ -718,14 +718,6 @@ static struct miscdevice isst_if_char_driver = {
|
||||
.fops = &isst_if_char_driver_ops,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id hpm_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_D, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_CRESTMONT, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_CRESTMONT_X, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
static int isst_misc_reg(void)
|
||||
{
|
||||
mutex_lock(&punit_misc_dev_reg_lock);
|
||||
@ -733,12 +725,6 @@ static int isst_misc_reg(void)
|
||||
goto unlock_exit;
|
||||
|
||||
if (!misc_usage_count) {
|
||||
const struct x86_cpu_id *id;
|
||||
|
||||
id = x86_match_cpu(hpm_cpu_ids);
|
||||
if (id)
|
||||
isst_hpm_support = true;
|
||||
|
||||
misc_device_ret = isst_if_cpu_info_init();
|
||||
if (misc_device_ret)
|
||||
goto unlock_exit;
|
||||
@ -786,11 +772,12 @@ static void isst_misc_unreg(void)
|
||||
*/
|
||||
int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (device_type >= ISST_IF_DEV_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (device_type < ISST_IF_DEV_TPMI && isst_hpm_support)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&punit_misc_dev_open_lock);
|
||||
/* Device is already open, we don't want to add new callbacks */
|
||||
if (misc_device_open) {
|
||||
@ -805,15 +792,6 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
|
||||
punit_callbacks[device_type].registered = 1;
|
||||
mutex_unlock(&punit_misc_dev_open_lock);
|
||||
|
||||
ret = isst_misc_reg();
|
||||
if (ret) {
|
||||
/*
|
||||
* No need of mutex as the misc device register failed
|
||||
* as no one can open device yet. Hence no contention.
|
||||
*/
|
||||
punit_callbacks[device_type].registered = 0;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(isst_if_cdev_register);
|
||||
@ -829,7 +807,6 @@ EXPORT_SYMBOL_GPL(isst_if_cdev_register);
|
||||
*/
|
||||
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;
|
||||
@ -839,5 +816,51 @@ void isst_if_cdev_unregister(int device_type)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
|
||||
|
||||
#define SST_HPM_SUPPORTED 0x01
|
||||
#define SST_MBOX_SUPPORTED 0x02
|
||||
|
||||
static const struct x86_cpu_id isst_cpu_ids[] = {
|
||||
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, SST_HPM_SUPPORTED),
|
||||
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, SST_HPM_SUPPORTED),
|
||||
X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, 0),
|
||||
X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, SST_HPM_SUPPORTED),
|
||||
X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, SST_HPM_SUPPORTED),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_D, 0),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_X, 0),
|
||||
X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, 0),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE_X, SST_MBOX_SUPPORTED),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, isst_cpu_ids);
|
||||
|
||||
static int __init isst_if_common_init(void)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
|
||||
id = x86_match_cpu(isst_cpu_ids);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
if (id->driver_data == SST_HPM_SUPPORTED) {
|
||||
isst_hpm_support = true;
|
||||
} else if (id->driver_data == SST_MBOX_SUPPORTED) {
|
||||
u64 data;
|
||||
|
||||
/* Can fail only on some Skylake-X generations */
|
||||
if (rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data) ||
|
||||
rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return isst_misc_reg();
|
||||
}
|
||||
module_init(isst_if_common_init)
|
||||
|
||||
static void __exit isst_if_common_exit(void)
|
||||
{
|
||||
isst_misc_unreg();
|
||||
}
|
||||
module_exit(isst_if_common_exit)
|
||||
|
||||
MODULE_DESCRIPTION("ISST common interface module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -16,6 +16,9 @@
|
||||
#define PCI_DEVICE_ID_INTEL_RAPL_PRIO_DEVID_1 0x3251
|
||||
#define PCI_DEVICE_ID_INTEL_CFG_MBOX_DEVID_1 0x3259
|
||||
|
||||
#define MSR_OS_MAILBOX_INTERFACE 0xB0
|
||||
#define MSR_OS_MAILBOX_DATA 0xB1
|
||||
|
||||
/*
|
||||
* Validate maximum commands in a single request.
|
||||
* This is enough to handle command to every core in one ioctl, or all
|
||||
|
@ -21,8 +21,6 @@
|
||||
|
||||
#include "isst_if_common.h"
|
||||
|
||||
#define MSR_OS_MAILBOX_INTERFACE 0xB0
|
||||
#define MSR_OS_MAILBOX_DATA 0xB1
|
||||
#define MSR_OS_MAILBOX_BUSY_BIT 31
|
||||
|
||||
/*
|
||||
@ -161,7 +159,7 @@ static struct notifier_block isst_pm_nb = {
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id isst_if_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
|
||||
|
@ -308,8 +308,8 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = {
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id telemetry_debugfs_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &telem_apl_debugfs_conf),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &telem_apl_debugfs_conf),
|
||||
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, &telem_apl_debugfs_conf),
|
||||
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_PLUS, &telem_apl_debugfs_conf),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, telemetry_debugfs_cpu_ids);
|
||||
|
@ -177,8 +177,8 @@ static struct telemetry_plt_config telem_glk_config = {
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id telemetry_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &telem_apl_config),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &telem_glk_config),
|
||||
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, &telem_apl_config),
|
||||
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_PLUS, &telem_glk_config),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -357,6 +357,15 @@ int tpmi_get_feature_status(struct auxiliary_device *auxdev,
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI);
|
||||
|
||||
struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent);
|
||||
struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev);
|
||||
|
||||
return tpmi_info->dbgfs_dir;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_debugfs_dir, INTEL_TPMI);
|
||||
|
||||
static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct intel_tpmi_info *tpmi_info = s->private;
|
||||
@ -577,6 +586,8 @@ static const char *intel_tpmi_name(enum intel_tpmi_id id)
|
||||
return "uncore";
|
||||
case TPMI_ID_SST:
|
||||
return "sst";
|
||||
case TPMI_ID_PLR:
|
||||
return "plr";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
235
drivers/platform/x86/intel/tpmi_power_domains.c
Normal file
235
drivers/platform/x86/intel/tpmi_power_domains.c
Normal file
@ -0,0 +1,235 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Mapping of TPMI power domains CPU mapping
|
||||
*
|
||||
* Copyright (c) 2024, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/topology.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/msr.h>
|
||||
|
||||
#include "tpmi_power_domains.h"
|
||||
|
||||
#define MSR_PM_LOGICAL_ID 0x54
|
||||
|
||||
/*
|
||||
* Struct of MSR 0x54
|
||||
* [15:11] PM_DOMAIN_ID
|
||||
* [10:3] MODULE_ID (aka IDI_AGENT_ID)
|
||||
* [2:0] LP_ID
|
||||
* For Atom:
|
||||
* [2] Always 0
|
||||
* [1:0] core ID within module
|
||||
* For Core
|
||||
* [2:1] Always 0
|
||||
* [0] thread ID
|
||||
*/
|
||||
|
||||
#define LP_ID_MASK GENMASK_ULL(2, 0)
|
||||
#define MODULE_ID_MASK GENMASK_ULL(10, 3)
|
||||
#define PM_DOMAIN_ID_MASK GENMASK_ULL(15, 11)
|
||||
|
||||
/**
|
||||
* struct tpmi_cpu_info - Mapping information for a CPU
|
||||
* @hnode: Used to add mapping information to hash list
|
||||
* @linux_cpu: Linux CPU number
|
||||
* @pkg_id: Package ID of this CPU
|
||||
* @punit_thread_id: Punit thread id of this CPU
|
||||
* @punit_core_id: Punit core id
|
||||
* @punit_domain_id: Power domain id from Punit
|
||||
*
|
||||
* Structure to store mapping information for a Linux CPU
|
||||
* to a Punit core, thread and power domain.
|
||||
*/
|
||||
struct tpmi_cpu_info {
|
||||
struct hlist_node hnode;
|
||||
int linux_cpu;
|
||||
u8 pkg_id;
|
||||
u8 punit_thread_id;
|
||||
u8 punit_core_id;
|
||||
u8 punit_domain_id;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct tpmi_cpu_info, tpmi_cpu_info);
|
||||
|
||||
/* The dynamically assigned cpu hotplug state to free later */
|
||||
static enum cpuhp_state tpmi_hp_state __read_mostly;
|
||||
|
||||
#define MAX_POWER_DOMAINS 8
|
||||
|
||||
static cpumask_t *tpmi_power_domain_mask;
|
||||
|
||||
/* Lock to protect tpmi_power_domain_mask and tpmi_cpu_hash */
|
||||
static DEFINE_MUTEX(tpmi_lock);
|
||||
|
||||
static const struct x86_cpu_id tpmi_cpu_ids[] = {
|
||||
X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, NULL),
|
||||
X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, NULL),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, tpmi_cpu_ids);
|
||||
|
||||
static DECLARE_HASHTABLE(tpmi_cpu_hash, 8);
|
||||
|
||||
static bool tpmi_domain_is_valid(struct tpmi_cpu_info *info)
|
||||
{
|
||||
return info->pkg_id < topology_max_packages() &&
|
||||
info->punit_domain_id < MAX_POWER_DOMAINS;
|
||||
}
|
||||
|
||||
int tpmi_get_linux_cpu_number(int package_id, int domain_id, int punit_core_id)
|
||||
{
|
||||
struct tpmi_cpu_info *info;
|
||||
int ret = -EINVAL;
|
||||
|
||||
guard(mutex)(&tpmi_lock);
|
||||
hash_for_each_possible(tpmi_cpu_hash, info, hnode, punit_core_id) {
|
||||
if (info->punit_domain_id == domain_id && info->pkg_id == package_id) {
|
||||
ret = info->linux_cpu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_linux_cpu_number, INTEL_TPMI_POWER_DOMAIN);
|
||||
|
||||
int tpmi_get_punit_core_number(int cpu_no)
|
||||
{
|
||||
if (cpu_no >= num_possible_cpus())
|
||||
return -EINVAL;
|
||||
|
||||
return per_cpu(tpmi_cpu_info, cpu_no).punit_core_id;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_punit_core_number, INTEL_TPMI_POWER_DOMAIN);
|
||||
|
||||
int tpmi_get_power_domain_id(int cpu_no)
|
||||
{
|
||||
if (cpu_no >= num_possible_cpus())
|
||||
return -EINVAL;
|
||||
|
||||
return per_cpu(tpmi_cpu_info, cpu_no).punit_domain_id;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_id, INTEL_TPMI_POWER_DOMAIN);
|
||||
|
||||
cpumask_t *tpmi_get_power_domain_mask(int cpu_no)
|
||||
{
|
||||
struct tpmi_cpu_info *info;
|
||||
cpumask_t *mask;
|
||||
int index;
|
||||
|
||||
if (cpu_no >= num_possible_cpus())
|
||||
return NULL;
|
||||
|
||||
info = &per_cpu(tpmi_cpu_info, cpu_no);
|
||||
if (!tpmi_domain_is_valid(info))
|
||||
return NULL;
|
||||
|
||||
index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id;
|
||||
guard(mutex)(&tpmi_lock);
|
||||
mask = &tpmi_power_domain_mask[index];
|
||||
|
||||
return mask;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_mask, INTEL_TPMI_POWER_DOMAIN);
|
||||
|
||||
static int tpmi_get_logical_id(unsigned int cpu, struct tpmi_cpu_info *info)
|
||||
{
|
||||
u64 data;
|
||||
int ret;
|
||||
|
||||
ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->punit_domain_id = FIELD_GET(PM_DOMAIN_ID_MASK, data);
|
||||
if (info->punit_domain_id >= MAX_POWER_DOMAINS)
|
||||
return -EINVAL;
|
||||
|
||||
info->punit_thread_id = FIELD_GET(LP_ID_MASK, data);
|
||||
info->punit_core_id = FIELD_GET(MODULE_ID_MASK, data);
|
||||
info->pkg_id = topology_physical_package_id(cpu);
|
||||
info->linux_cpu = cpu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpmi_cpu_online(unsigned int cpu)
|
||||
{
|
||||
struct tpmi_cpu_info *info = &per_cpu(tpmi_cpu_info, cpu);
|
||||
int ret, index;
|
||||
|
||||
/* Don't fail CPU online for some bad mapping of CPUs */
|
||||
ret = tpmi_get_logical_id(cpu, info);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id;
|
||||
|
||||
guard(mutex)(&tpmi_lock);
|
||||
cpumask_set_cpu(cpu, &tpmi_power_domain_mask[index]);
|
||||
hash_add(tpmi_cpu_hash, &info->hnode, info->punit_core_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init tpmi_init(void)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
u64 data;
|
||||
int ret;
|
||||
|
||||
id = x86_match_cpu(tpmi_cpu_ids);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check for MSR 0x54 presence */
|
||||
ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tpmi_power_domain_mask = kcalloc(size_mul(topology_max_packages(), MAX_POWER_DOMAINS),
|
||||
sizeof(*tpmi_power_domain_mask), GFP_KERNEL);
|
||||
if (!tpmi_power_domain_mask)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
|
||||
"platform/x86/tpmi_power_domains:online",
|
||||
tpmi_cpu_online, NULL);
|
||||
if (ret < 0) {
|
||||
kfree(tpmi_power_domain_mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tpmi_hp_state = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(tpmi_init)
|
||||
|
||||
static void __exit tpmi_exit(void)
|
||||
{
|
||||
cpuhp_remove_state(tpmi_hp_state);
|
||||
kfree(tpmi_power_domain_mask);
|
||||
}
|
||||
module_exit(tpmi_exit)
|
||||
|
||||
MODULE_DESCRIPTION("TPMI Power Domains Mapping");
|
||||
MODULE_LICENSE("GPL");
|
18
drivers/platform/x86/intel/tpmi_power_domains.h
Normal file
18
drivers/platform/x86/intel/tpmi_power_domains.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Mapping of TPMI power domain and CPUs
|
||||
*
|
||||
* Copyright (c) 2024, Intel Corporation.
|
||||
*/
|
||||
|
||||
#ifndef _TPMI_POWER_DOMAINS_H_
|
||||
#define _TPMI_POWER_DOMAINS_H_
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
|
||||
int tpmi_get_linux_cpu_number(int package_id, int die_id, int punit_core_id);
|
||||
int tpmi_get_punit_core_number(int cpu_no);
|
||||
int tpmi_get_power_domain_id(int cpu_no);
|
||||
cpumask_t *tpmi_get_power_domain_mask(int cpu_no);
|
||||
|
||||
#endif
|
@ -114,8 +114,8 @@ static int itmt_legacy_cpu_online(unsigned int cpu)
|
||||
}
|
||||
|
||||
static const struct x86_cpu_id itmt_legacy_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_BROADWELL_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -19,9 +19,8 @@ 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 int (*uncore_read)(struct uncore_data *data, unsigned int *value, enum uncore_index index);
|
||||
static int (*uncore_write)(struct uncore_data *data, unsigned int input, enum uncore_index index);
|
||||
|
||||
static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
@ -44,27 +43,22 @@ static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *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)
|
||||
static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index index)
|
||||
{
|
||||
unsigned int min, max;
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&uncore_lock);
|
||||
ret = uncore_read(data, &min, &max);
|
||||
ret = uncore_read(data, &value, index);
|
||||
mutex_unlock(&uncore_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (min_max)
|
||||
return sprintf(buf, "%u\n", max);
|
||||
|
||||
return sprintf(buf, "%u\n", min);
|
||||
return sprintf(buf, "%u\n", value);
|
||||
}
|
||||
|
||||
static ssize_t store_min_max_freq_khz(struct uncore_data *data,
|
||||
const char *buf, ssize_t count,
|
||||
int min_max)
|
||||
static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count,
|
||||
enum uncore_index index)
|
||||
{
|
||||
unsigned int input;
|
||||
int ret;
|
||||
@ -73,7 +67,7 @@ static ssize_t store_min_max_freq_khz(struct uncore_data *data,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&uncore_lock);
|
||||
ret = uncore_write(data, input, min_max);
|
||||
ret = uncore_write(data, input, index);
|
||||
mutex_unlock(&uncore_lock);
|
||||
|
||||
if (ret)
|
||||
@ -82,56 +76,32 @@ static ssize_t store_min_max_freq_khz(struct uncore_data *data,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf)
|
||||
{
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&uncore_lock);
|
||||
ret = uncore_read_freq(data, &freq);
|
||||
mutex_unlock(&uncore_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", freq);
|
||||
}
|
||||
|
||||
#define store_uncore_min_max(name, min_max) \
|
||||
#define store_uncore_attr(name, index) \
|
||||
static ssize_t store_##name(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
|
||||
\
|
||||
return store_min_max_freq_khz(data, buf, count, \
|
||||
min_max); \
|
||||
return store_attr(data, buf, count, index); \
|
||||
}
|
||||
|
||||
#define show_uncore_min_max(name, min_max) \
|
||||
#define show_uncore_attr(name, index) \
|
||||
static ssize_t show_##name(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, char *buf)\
|
||||
{ \
|
||||
struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
|
||||
\
|
||||
return show_min_max_freq_khz(data, buf, min_max); \
|
||||
return show_attr(data, buf, index); \
|
||||
}
|
||||
|
||||
#define show_uncore_perf_status(name) \
|
||||
static ssize_t show_##name(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, char *buf)\
|
||||
{ \
|
||||
struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
|
||||
\
|
||||
return show_perf_status_freq_khz(data, buf); \
|
||||
}
|
||||
store_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
|
||||
store_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
|
||||
|
||||
store_uncore_min_max(min_freq_khz, 0);
|
||||
store_uncore_min_max(max_freq_khz, 1);
|
||||
show_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ);
|
||||
show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ);
|
||||
|
||||
show_uncore_min_max(min_freq_khz, 0);
|
||||
show_uncore_min_max(max_freq_khz, 1);
|
||||
|
||||
show_uncore_perf_status(current_freq_khz);
|
||||
show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ);
|
||||
|
||||
#define show_uncore_data(member_name) \
|
||||
static ssize_t show_##member_name(struct kobject *kobj, \
|
||||
@ -198,7 +168,7 @@ static int create_attr_group(struct uncore_data *data, char *name)
|
||||
data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr;
|
||||
data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr;
|
||||
|
||||
ret = uncore_read_freq(data, &freq);
|
||||
ret = uncore_read(data, &freq, UNCORE_INDEX_CURRENT_FREQ);
|
||||
if (!ret)
|
||||
data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr;
|
||||
|
||||
@ -238,7 +208,8 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu)
|
||||
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);
|
||||
uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
|
||||
uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ);
|
||||
|
||||
ret = create_attr_group(data, data->name);
|
||||
if (ret) {
|
||||
@ -269,15 +240,15 @@ void uncore_freq_remove_die_entry(struct uncore_data *data)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
|
||||
|
||||
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 set_max),
|
||||
int (*read_freq)(struct uncore_data *data, unsigned int *freq))
|
||||
int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value,
|
||||
enum uncore_index index),
|
||||
int (*write)(struct uncore_data *data, unsigned int input,
|
||||
enum uncore_index index))
|
||||
{
|
||||
mutex_lock(&uncore_lock);
|
||||
|
||||
uncore_read = read_control_freq;
|
||||
uncore_write = write_control_freq;
|
||||
uncore_read_freq = read_freq;
|
||||
uncore_read = read;
|
||||
uncore_write = write;
|
||||
|
||||
if (!uncore_root_kobj) {
|
||||
struct device *dev_root = bus_get_dev_root(&cpu_subsys);
|
||||
|
@ -66,9 +66,16 @@ struct uncore_data {
|
||||
|
||||
#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));
|
||||
enum uncore_index {
|
||||
UNCORE_INDEX_MIN_FREQ,
|
||||
UNCORE_INDEX_MAX_FREQ,
|
||||
UNCORE_INDEX_CURRENT_FREQ,
|
||||
};
|
||||
|
||||
int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value,
|
||||
enum uncore_index index),
|
||||
int (*write)(struct uncore_data *data, unsigned int input,
|
||||
enum uncore_index index));
|
||||
void uncore_freq_common_exit(void);
|
||||
int uncore_freq_add_entry(struct uncore_data *data, int cpu);
|
||||
void uncore_freq_remove_die_entry(struct uncore_data *data);
|
||||
|
@ -69,26 +69,31 @@ struct tpmi_uncore_struct {
|
||||
bool write_blocked;
|
||||
};
|
||||
|
||||
#define UNCORE_GENMASK_MIN_RATIO GENMASK_ULL(21, 15)
|
||||
#define UNCORE_GENMASK_MAX_RATIO GENMASK_ULL(14, 8)
|
||||
#define UNCORE_GENMASK_CURRENT_RATIO GENMASK_ULL(6, 0)
|
||||
/* Bit definitions for STATUS register */
|
||||
#define UNCORE_CURRENT_RATIO_MASK GENMASK_ULL(6, 0)
|
||||
|
||||
/* Bit definitions for CONTROL register */
|
||||
#define UNCORE_MAX_RATIO_MASK GENMASK_ULL(14, 8)
|
||||
#define UNCORE_MIN_RATIO_MASK GENMASK_ULL(21, 15)
|
||||
|
||||
/* Helper function to read MMIO offset for max/min control frequency */
|
||||
static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info,
|
||||
unsigned int *min, unsigned int *max)
|
||||
unsigned int *value, enum uncore_index index)
|
||||
{
|
||||
u64 control;
|
||||
|
||||
control = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX);
|
||||
*max = FIELD_GET(UNCORE_GENMASK_MAX_RATIO, control) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
*min = FIELD_GET(UNCORE_GENMASK_MIN_RATIO, control) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
if (index == UNCORE_INDEX_MAX_FREQ)
|
||||
*value = FIELD_GET(UNCORE_MAX_RATIO_MASK, control) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
else
|
||||
*value = FIELD_GET(UNCORE_MIN_RATIO_MASK, control) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
}
|
||||
|
||||
#define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_GENMASK_MAX_RATIO)
|
||||
#define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_MAX_RATIO_MASK)
|
||||
|
||||
/* Callback for sysfs read for max/min frequencies. Called under mutex locks */
|
||||
static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
|
||||
unsigned int *max)
|
||||
/* Helper for sysfs read for max/min frequencies. Called under mutex locks */
|
||||
static int uncore_read_control_freq(struct uncore_data *data, unsigned int *value,
|
||||
enum uncore_index index)
|
||||
{
|
||||
struct tpmi_uncore_cluster_info *cluster_info;
|
||||
|
||||
@ -96,10 +101,11 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
|
||||
|
||||
if (cluster_info->root_domain) {
|
||||
struct tpmi_uncore_struct *uncore_root = cluster_info->uncore_root;
|
||||
int i, _min = 0, _max = 0;
|
||||
unsigned int min, max, v;
|
||||
int i;
|
||||
|
||||
*min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
*max = 0;
|
||||
min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
max = 0;
|
||||
|
||||
/*
|
||||
* Get the max/min by looking at each cluster. Get the lowest
|
||||
@ -110,35 +116,41 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
|
||||
|
||||
for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j) {
|
||||
read_control_freq(&uncore_root->pd_info[i].cluster_infos[j],
|
||||
&_min, &_max);
|
||||
if (*min > _min)
|
||||
*min = _min;
|
||||
if (*max < _max)
|
||||
*max = _max;
|
||||
&v, index);
|
||||
if (v < min)
|
||||
min = v;
|
||||
if (v > max)
|
||||
max = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == UNCORE_INDEX_MIN_FREQ)
|
||||
*value = min;
|
||||
else
|
||||
*value = max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_control_freq(cluster_info, min, max);
|
||||
read_control_freq(cluster_info, value, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to write MMIO offset for max/min control frequency */
|
||||
static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, unsigned int input,
|
||||
unsigned int min_max)
|
||||
unsigned int index)
|
||||
{
|
||||
u64 control;
|
||||
|
||||
control = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX);
|
||||
|
||||
if (min_max) {
|
||||
control &= ~UNCORE_GENMASK_MAX_RATIO;
|
||||
control |= FIELD_PREP(UNCORE_GENMASK_MAX_RATIO, input);
|
||||
if (index == UNCORE_INDEX_MAX_FREQ) {
|
||||
control &= ~UNCORE_MAX_RATIO_MASK;
|
||||
control |= FIELD_PREP(UNCORE_MAX_RATIO_MASK, input);
|
||||
} else {
|
||||
control &= ~UNCORE_GENMASK_MIN_RATIO;
|
||||
control |= FIELD_PREP(UNCORE_GENMASK_MIN_RATIO, input);
|
||||
control &= ~UNCORE_MIN_RATIO_MASK;
|
||||
control |= FIELD_PREP(UNCORE_MIN_RATIO_MASK, input);
|
||||
}
|
||||
|
||||
writeq(control, (cluster_info->cluster_base + UNCORE_CONTROL_INDEX));
|
||||
@ -146,7 +158,7 @@ static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, un
|
||||
|
||||
/* Callback for sysfs write for max/min frequencies. Called under mutex locks */
|
||||
static int uncore_write_control_freq(struct uncore_data *data, unsigned int input,
|
||||
unsigned int min_max)
|
||||
enum uncore_index index)
|
||||
{
|
||||
struct tpmi_uncore_cluster_info *cluster_info;
|
||||
struct tpmi_uncore_struct *uncore_root;
|
||||
@ -171,10 +183,10 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu
|
||||
|
||||
for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j)
|
||||
write_control_freq(&uncore_root->pd_info[i].cluster_infos[j],
|
||||
input, min_max);
|
||||
input, index);
|
||||
}
|
||||
|
||||
if (min_max)
|
||||
if (index == UNCORE_INDEX_MAX_FREQ)
|
||||
uncore_root->max_ratio = input;
|
||||
else
|
||||
uncore_root->min_ratio = input;
|
||||
@ -182,18 +194,20 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (min_max && uncore_root->max_ratio && uncore_root->max_ratio < input)
|
||||
if (index == UNCORE_INDEX_MAX_FREQ && uncore_root->max_ratio &&
|
||||
uncore_root->max_ratio < input)
|
||||
return -EINVAL;
|
||||
|
||||
if (!min_max && uncore_root->min_ratio && uncore_root->min_ratio > input)
|
||||
if (index == UNCORE_INDEX_MIN_FREQ && uncore_root->min_ratio &&
|
||||
uncore_root->min_ratio > input)
|
||||
return -EINVAL;
|
||||
|
||||
write_control_freq(cluster_info, input, min_max);
|
||||
write_control_freq(cluster_info, input, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback for sysfs read for the current uncore frequency. Called under mutex locks */
|
||||
/* Helper for sysfs read for the current uncore frequency. Called under mutex locks */
|
||||
static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
|
||||
{
|
||||
struct tpmi_uncore_cluster_info *cluster_info;
|
||||
@ -204,11 +218,29 @@ static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
|
||||
return -ENODATA;
|
||||
|
||||
status = readq((u8 __iomem *)cluster_info->cluster_base + UNCORE_STATUS_INDEX);
|
||||
*freq = FIELD_GET(UNCORE_GENMASK_CURRENT_RATIO, status) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
*freq = FIELD_GET(UNCORE_CURRENT_RATIO_MASK, status) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback for sysfs read for TPMI uncore values. Called under mutex locks. */
|
||||
static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncore_index index)
|
||||
{
|
||||
switch (index) {
|
||||
case UNCORE_INDEX_MIN_FREQ:
|
||||
case UNCORE_INDEX_MAX_FREQ:
|
||||
return uncore_read_control_freq(data, value, index);
|
||||
|
||||
case UNCORE_INDEX_CURRENT_FREQ:
|
||||
return uncore_read_freq(data, value);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void remove_cluster_entries(struct tpmi_uncore_struct *tpmi_uncore)
|
||||
{
|
||||
int i;
|
||||
@ -259,8 +291,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
|
||||
return -EINVAL;
|
||||
|
||||
/* Register callbacks to uncore core */
|
||||
ret = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq,
|
||||
uncore_read_freq);
|
||||
ret = uncore_freq_common_init(uncore_read, uncore_write_control_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
@ -36,8 +37,13 @@ static enum cpuhp_state uncore_hp_state __read_mostly;
|
||||
#define MSR_UNCORE_PERF_STATUS 0x621
|
||||
#define UNCORE_FREQ_KHZ_MULTIPLIER 100000
|
||||
|
||||
static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
|
||||
unsigned int *max)
|
||||
#define UNCORE_MAX_RATIO_MASK GENMASK_ULL(6, 0)
|
||||
#define UNCORE_MIN_RATIO_MASK GENMASK_ULL(14, 8)
|
||||
|
||||
#define UNCORE_CURRENT_RATIO_MASK GENMASK_ULL(6, 0)
|
||||
|
||||
static int uncore_read_control_freq(struct uncore_data *data, unsigned int *value,
|
||||
enum uncore_index index)
|
||||
{
|
||||
u64 cap;
|
||||
int ret;
|
||||
@ -49,20 +55,22 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
*min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
if (index == UNCORE_INDEX_MAX_FREQ)
|
||||
*value = FIELD_GET(UNCORE_MAX_RATIO_MASK, cap) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
else
|
||||
*value = FIELD_GET(UNCORE_MIN_RATIO_MASK, cap) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uncore_write_control_freq(struct uncore_data *data, unsigned int input,
|
||||
unsigned int min_max)
|
||||
enum uncore_index index)
|
||||
{
|
||||
int ret;
|
||||
u64 cap;
|
||||
|
||||
input /= UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
if (!input || input > 0x7F)
|
||||
if (!input || input > FIELD_MAX(UNCORE_MAX_RATIO_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
if (data->control_cpu < 0)
|
||||
@ -72,12 +80,12 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (min_max) {
|
||||
cap &= ~0x7F;
|
||||
cap |= input;
|
||||
if (index == UNCORE_INDEX_MAX_FREQ) {
|
||||
cap &= ~UNCORE_MAX_RATIO_MASK;
|
||||
cap |= FIELD_PREP(UNCORE_MAX_RATIO_MASK, input);
|
||||
} else {
|
||||
cap &= ~GENMASK(14, 8);
|
||||
cap |= (input << 8);
|
||||
cap &= ~UNCORE_MIN_RATIO_MASK;
|
||||
cap |= FIELD_PREP(UNCORE_MIN_RATIO_MASK, input);
|
||||
}
|
||||
|
||||
ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap);
|
||||
@ -101,11 +109,28 @@ static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*freq = (ratio & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
*freq = FIELD_GET(UNCORE_CURRENT_RATIO_MASK, ratio) * UNCORE_FREQ_KHZ_MULTIPLIER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncore_index index)
|
||||
{
|
||||
switch (index) {
|
||||
case UNCORE_INDEX_MIN_FREQ:
|
||||
case UNCORE_INDEX_MAX_FREQ:
|
||||
return uncore_read_control_freq(data, value, index);
|
||||
|
||||
case UNCORE_INDEX_CURRENT_FREQ:
|
||||
return uncore_read_freq(data, value);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Caller provides protection */
|
||||
static struct uncore_data *uncore_get_instance(unsigned int cpu)
|
||||
{
|
||||
@ -197,34 +222,34 @@ static struct notifier_block uncore_pm_nb = {
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE_H, NULL),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, NULL),
|
||||
X86_MATCH_VFM(INTEL_BROADWELL_G, NULL),
|
||||
X86_MATCH_VFM(INTEL_BROADWELL_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_BROADWELL_D, NULL),
|
||||
X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_D, NULL),
|
||||
X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, NULL),
|
||||
X86_MATCH_VFM(INTEL_KABYLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_KABYLAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_COMETLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_COMETLAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_CANNONLAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_ICELAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_ROCKETLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_TIGERLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_TIGERLAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_ALDERLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_ALDERLAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL),
|
||||
X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL),
|
||||
X86_MATCH_VFM(INTEL_METEORLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_METEORLAKE_L, NULL),
|
||||
X86_MATCH_VFM(INTEL_ARROWLAKE, NULL),
|
||||
X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL),
|
||||
X86_MATCH_VFM(INTEL_LUNARLAKE_M, NULL),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids);
|
||||
@ -248,8 +273,7 @@ static int __init intel_uncore_init(void)
|
||||
if (!uncore_instances)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq,
|
||||
uncore_read_freq);
|
||||
ret = uncore_freq_common_init(uncore_read, uncore_write_control_freq);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include <drm/i915_drm.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include "intel_ips.h"
|
||||
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
@ -1284,7 +1285,7 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
|
||||
struct ips_mcp_limits *limits = NULL;
|
||||
u16 tdp;
|
||||
|
||||
if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) {
|
||||
if (!(boot_cpu_data.x86_vfm == INTEL_WESTMERE)) {
|
||||
dev_info(ips->dev, "Non-IPS CPU detected.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ static struct intel_mid_wdt_pdata tangier_pdata = {
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id intel_mid_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &tangier_pdata),
|
||||
X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID, &tangier_pdata),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
#define SPI_DEVFN_GOLDMONT PCI_DEVFN(13, 2)
|
||||
|
||||
static const struct x86_cpu_id p2sb_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, P2SB_DEVFN_GOLDMONT),
|
||||
X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, P2SB_DEVFN_GOLDMONT),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -131,7 +131,7 @@ static int smi_spi_probe(struct platform_device *pdev, struct smi *smi,
|
||||
|
||||
ctlr = spi_dev->controller;
|
||||
|
||||
strscpy(spi_dev->modalias, inst_array[i].type, sizeof(spi_dev->modalias));
|
||||
strscpy(spi_dev->modalias, inst_array[i].type);
|
||||
|
||||
ret = smi_get_irq(pdev, adev, &inst_array[i]);
|
||||
if (ret < 0) {
|
||||
@ -205,7 +205,7 @@ static int smi_i2c_probe(struct platform_device *pdev, struct smi *smi,
|
||||
|
||||
for (i = 0; i < count && inst_array[i].type; i++) {
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strscpy(board_info.type, inst_array[i].type, I2C_NAME_SIZE);
|
||||
strscpy(board_info.type, inst_array[i].type);
|
||||
snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i);
|
||||
board_info.dev_name = name;
|
||||
|
||||
|
@ -1508,7 +1508,7 @@ static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type,
|
||||
if (!new_pwd)
|
||||
return NULL;
|
||||
|
||||
strscpy(new_pwd->kbdlang, "us", TLMI_LANG_MAXLEN);
|
||||
strscpy(new_pwd->kbdlang, "us");
|
||||
new_pwd->encoding = TLMI_ENCODING_ASCII;
|
||||
new_pwd->pwd_type = pwd_type;
|
||||
new_pwd->role = pwd_role;
|
||||
@ -1582,7 +1582,7 @@ static int tlmi_analyze(void)
|
||||
goto fail_clear_attr;
|
||||
}
|
||||
setting->index = i;
|
||||
strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN);
|
||||
strscpy(setting->display_name, item);
|
||||
/* If BIOS selections supported, load those */
|
||||
if (tlmi_priv.can_get_bios_selections) {
|
||||
ret = tlmi_get_bios_selections(setting->display_name,
|
||||
|
@ -7416,10 +7416,8 @@ static int __init volume_create_alsa_mixer(void)
|
||||
data = card->private_data;
|
||||
data->card = card;
|
||||
|
||||
strscpy(card->driver, TPACPI_ALSA_DRVNAME,
|
||||
sizeof(card->driver));
|
||||
strscpy(card->shortname, TPACPI_ALSA_SHRTNAME,
|
||||
sizeof(card->shortname));
|
||||
strscpy(card->driver, TPACPI_ALSA_DRVNAME);
|
||||
strscpy(card->shortname, TPACPI_ALSA_SHRTNAME);
|
||||
snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s",
|
||||
(thinkpad_id.ec_version_str) ?
|
||||
thinkpad_id.ec_version_str : "(unknown)");
|
||||
|
@ -772,11 +772,39 @@ static ssize_t expensive_show(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RO(expensive);
|
||||
|
||||
static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wmi_device *wdev = to_wmi_device(dev);
|
||||
ssize_t ret;
|
||||
|
||||
device_lock(dev);
|
||||
ret = sysfs_emit(buf, "%s\n", wdev->driver_override);
|
||||
device_unlock(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t driver_override_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct wmi_device *wdev = to_wmi_device(dev);
|
||||
int ret;
|
||||
|
||||
ret = driver_set_override(dev, &wdev->driver_override, buf, count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(driver_override);
|
||||
|
||||
static struct attribute *wmi_attrs[] = {
|
||||
&dev_attr_modalias.attr,
|
||||
&dev_attr_guid.attr,
|
||||
&dev_attr_instance_count.attr,
|
||||
&dev_attr_expensive.attr,
|
||||
&dev_attr_driver_override.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wmi);
|
||||
@ -845,6 +873,7 @@ static void wmi_dev_release(struct device *dev)
|
||||
{
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
|
||||
kfree(wblock->dev.driver_override);
|
||||
kfree(wblock);
|
||||
}
|
||||
|
||||
@ -854,6 +883,10 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
|
||||
struct wmi_block *wblock = dev_to_wblock(dev);
|
||||
const struct wmi_device_id *id = wmi_driver->id_table;
|
||||
|
||||
/* When driver_override is set, only bind to the matching driver */
|
||||
if (wblock->dev.driver_override)
|
||||
return !strcmp(wblock->dev.driver_override, driver->name);
|
||||
|
||||
if (id == NULL)
|
||||
return 0;
|
||||
|
||||
|
@ -21,6 +21,7 @@ enum intel_tpmi_id {
|
||||
TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */
|
||||
TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */
|
||||
TPMI_ID_SST = 5, /* Speed Select Technology */
|
||||
TPMI_ID_PLR = 0xc, /* Performance Limit Reasons */
|
||||
TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */
|
||||
TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
|
||||
};
|
||||
@ -53,4 +54,5 @@ struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int
|
||||
int tpmi_get_resource_count(struct auxiliary_device *auxdev);
|
||||
int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked,
|
||||
bool *write_blocked);
|
||||
struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev);
|
||||
#endif
|
||||
|
44
include/linux/platform_data/lenovo-yoga-c630.h
Normal file
44
include/linux/platform_data/lenovo-yoga-c630.h
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022-2024, Linaro Ltd
|
||||
* Authors:
|
||||
* Bjorn Andersson
|
||||
* Dmitry Baryshkov
|
||||
*/
|
||||
|
||||
#ifndef _LENOVO_YOGA_C630_DATA_H
|
||||
#define _LENOVO_YOGA_C630_DATA_H
|
||||
|
||||
struct yoga_c630_ec;
|
||||
struct notifier_block;
|
||||
|
||||
#define YOGA_C630_MOD_NAME "lenovo_yoga_c630"
|
||||
|
||||
#define YOGA_C630_DEV_UCSI "ucsi"
|
||||
#define YOGA_C630_DEV_PSY "psy"
|
||||
|
||||
int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr);
|
||||
int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr);
|
||||
|
||||
int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb);
|
||||
void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb);
|
||||
|
||||
#define YOGA_C630_UCSI_WRITE_SIZE 8
|
||||
#define YOGA_C630_UCSI_CCI_SIZE 4
|
||||
#define YOGA_C630_UCSI_DATA_SIZE 16
|
||||
#define YOGA_C630_UCSI_READ_SIZE (YOGA_C630_UCSI_CCI_SIZE + YOGA_C630_UCSI_DATA_SIZE)
|
||||
|
||||
u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec);
|
||||
int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec,
|
||||
const u8 req[YOGA_C630_UCSI_WRITE_SIZE]);
|
||||
int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec,
|
||||
u8 resp[YOGA_C630_UCSI_READ_SIZE]);
|
||||
|
||||
#define LENOVO_EC_EVENT_USB 0x20
|
||||
#define LENOVO_EC_EVENT_UCSI 0x21
|
||||
#define LENOVO_EC_EVENT_HPD 0x22
|
||||
#define LENOVO_EC_EVENT_BAT_STATUS 0x24
|
||||
#define LENOVO_EC_EVENT_BAT_INFO 0x25
|
||||
#define LENOVO_EC_EVENT_BAT_ADPT_STATUS 0x37
|
||||
|
||||
#endif
|
@ -51,6 +51,10 @@
|
||||
#define ASUS_WMI_DEVID_LED6 0x00020016
|
||||
#define ASUS_WMI_DEVID_MICMUTE_LED 0x00040017
|
||||
|
||||
/* Disable Camera LED */
|
||||
#define ASUS_WMI_DEVID_CAMERA_LED_NEG 0x00060078 /* 0 = on (unused) */
|
||||
#define ASUS_WMI_DEVID_CAMERA_LED 0x00060079 /* 1 = on */
|
||||
|
||||
/* Backlight and Brightness */
|
||||
#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */
|
||||
#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011
|
||||
|
@ -16,12 +16,16 @@
|
||||
* struct wmi_device - WMI device structure
|
||||
* @dev: Device associated with this WMI device
|
||||
* @setable: True for devices implementing the Set Control Method
|
||||
* @driver_override: Driver name to force a match; do not set directly,
|
||||
* because core frees it; use driver_set_override() to
|
||||
* set or clear it.
|
||||
*
|
||||
* This represents WMI devices discovered by the WMI driver core.
|
||||
*/
|
||||
struct wmi_device {
|
||||
struct device dev;
|
||||
bool setable;
|
||||
const char *driver_override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -16,7 +16,7 @@ struct process_cmd_struct {
|
||||
int arg;
|
||||
};
|
||||
|
||||
static const char *version_str = "v1.19";
|
||||
static const char *version_str = "v1.20";
|
||||
|
||||
static const int supported_api_ver = 3;
|
||||
static struct isst_if_platform_info isst_platform_info;
|
||||
|
@ -283,6 +283,8 @@ int isst_set_trl(struct isst_id *id, unsigned long long trl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MSR_TRL_FREQ_MULTIPLIER 100
|
||||
|
||||
int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl)
|
||||
{
|
||||
unsigned long long msr_trl;
|
||||
@ -310,6 +312,10 @@ int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl)
|
||||
for (i = 0; i < 8; ++i) {
|
||||
unsigned long long _trl = trl[i];
|
||||
|
||||
/* MSR is always in 100 MHz unit */
|
||||
if (isst_get_disp_freq_multiplier() == 1)
|
||||
_trl /= MSR_TRL_FREQ_MULTIPLIER;
|
||||
|
||||
msr_trl |= (_trl << (i * 8));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user