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:
Linus Torvalds 2024-07-17 17:05:21 -07:00
commit a5cb6b2bbf
63 changed files with 2299 additions and 522 deletions

View File

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

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

View File

@ -121,7 +121,6 @@ configure specific aspects of kernel behavior to your liking.
parport
perf-security
pm/index
pmf
pnp
rapidio
RAS/index

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,3 +6,4 @@
#
obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o

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

View File

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

View File

@ -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 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
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);

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -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 @@ int dell_smbios_call(struct calling_interface_buffer *buffer)
}
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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 @@ static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
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();

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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