Commit 5fda5698 authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull x86 platform driver updates from Hans de Goede:

 - Intel PMC / PMT / TPMI / uncore-freq / vsec improvements and new
   platform support

 - AMD PMC / PMF improvements and new platform support

 - AMD ACPI based Wifi band RFI mitigation feature (WBRF)

 - WMI bus driver cleanups and improvements (Armin Wolf)

 - acer-wmi Predator PHN16-71 support

 - New Silicom network appliance EC LEDs / GPIOs driver

* tag 'platform-drivers-x86-v6.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (96 commits)
  platform/x86/amd/pmc: Modify SMU message port for latest AMD platform
  platform/x86/amd/pmc: Add 1Ah family series to STB support list
  platform/x86/amd/pmc: Add idlemask support for 1Ah family
  platform/x86/amd/pmc: call amd_pmc_get_ip_info() during driver probe
  platform/x86/amd/pmc: Add VPE information for AMDI000A platform
  platform/x86/amd/pmc: Send OS_HINT command for AMDI000A platform
  platform/x86/amd/pmf: Return a status code only as a constant in two functions
  platform/x86/amd/pmf: Return directly after a failed apmf_if_call() in apmf_sbios_heartbeat_notify()
  platform/x86: wmi: linux/wmi.h: fix Excess kernel-doc description warning
  platform/x86/intel/pmc: Add missing extern
  platform/x86/intel/pmc/lnl: Add GBE LTR ignore during suspend
  platform/x86/intel/pmc/arl: Add GBE LTR ignore during suspend
  platform/x86: intel-uncore-freq: Add additional client processors
  platform/x86: Remove "X86 PLATFORM DRIVERS - ARCH" from MAINTAINERS
  platform/x86: hp-bioscfg: Removed needless asm-generic
  platform/x86/intel/pmc: Add Lunar Lake M support to intel_pmc_core driver
  platform/x86/intel/pmc: Add Arrow Lake S support to intel_pmc_core driver
  platform/x86/intel/pmc: Add ssram_init flag in PMC discovery in Meteor Lake
  platform/x86/intel/pmc: Move common code to core.c
  platform/x86/intel/pmc: Add PSON residency counter for Alder Lake
  ...
parents b9b56eb2 236f7d80
What: /sys/devices/platform/silicom-platform/uc_version
Date: November 2023
KernelVersion: 6.7
Contact: Henry Shi <henrys@silicom-usa.com>
Description:
This file allows to read microcontroller firmware
version of current platform.
What: /sys/devices/platform/silicom-platform/power_cycle
Date: November 2023
KernelVersion: 6.7
Contact: Henry Shi <henrys@silicom-usa.com>
This file allow user to power cycle the platform.
Default value is 0; when set to 1, it powers down
the platform, waits 5 seconds, then powers on the
device. It returns to default value after power cycle.
0 - default value.
What: /sys/devices/platform/silicom-platform/efuse_status
Date: November 2023
KernelVersion: 6.7
Contact: Henry Shi <henrys@silicom-usa.com>
Description:
This file is read only. It returns the current
OTP status:
0 - not programmed.
1 - programmed.
......@@ -119,6 +119,7 @@ configure specific aspects of kernel behavior to your liking.
parport
perf-security
pm/index
pmf
pnp
rapidio
ras
......
.. 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
......@@ -115,6 +115,7 @@ available subsections can be seen below.
hte/index
wmi
dpll
wbrf
.. only:: subproject and html
......
.. SPDX-License-Identifier: GPL-2.0-or-later
=================================
WBRF - Wifi Band RFI Mitigations
=================================
Due to electrical and mechanical constraints in certain platform designs
there may be likely interference of relatively high-powered harmonics of
the GPU memory clocks with local radio module frequency bands used by
certain Wifi bands.
To mitigate possible RFI interference producers can advertise the
frequencies in use and consumers can use this information to avoid using
these frequencies for sensitive features.
When a platform is known to have this issue with any contained devices,
the platform designer will advertise the availability of this feature via
ACPI devices with a device specific method (_DSM).
* Producers with this _DSM will be able to advertise the frequencies in use.
* Consumers with this _DSM will be able to register for notifications of
frequencies in use.
Some general terms
==================
Producer: such component who can produce high-powered radio frequency
Consumer: such component who can adjust its in-use frequency in
response to the radio frequencies of other components to mitigate the
possible RFI.
To make the mechanism function, those producers should notify active use
of their particular frequencies so that other consumers can make relative
internal adjustments as necessary to avoid this resonance.
ACPI interface
==============
Although initially used by for wifi + dGPU use cases, the ACPI interface
can be scaled to any type of device that a platform designer discovers
can cause interference.
The GUID used for the _DSM is 7B7656CF-DC3D-4C1C-83E9-66E721DE3070.
3 functions are available in this _DSM:
* 0: discover # of functions available
* 1: record RF bands in use
* 2: retrieve RF bands in use
Driver programming interface
============================
.. kernel-doc:: drivers/platform/x86/amd/wbrf.c
Sample Usage
=============
The expected flow for the producers:
1. During probe, call `acpi_amd_wbrf_supported_producer` to check if WBRF
can be enabled for the device.
2. On using some frequency band, call `acpi_amd_wbrf_add_remove` with 'add'
param to get other consumers properly notified.
3. Or on stopping using some frequency band, call
`acpi_amd_wbrf_add_remove` with 'remove' param to get other consumers notified.
The expected flow for the consumers:
1. During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
can be enabled for the device.
2. Call `amd_wbrf_register_notifier` to register for notification
of frequency band change(add or remove) from other producers.
3. Call the `amd_wbrf_retrieve_freq_band` initally to retrieve
current active frequency bands considering some producers may broadcast
such information before the consumer is up.
4. On receiving a notification for frequency band change, run
`amd_wbrf_retrieve_freq_band` again to retrieve the latest
active frequency bands.
5. During driver cleanup, call `amd_wbrf_unregister_notifier` to
unregister the notifier.
......@@ -23635,15 +23635,6 @@ F: drivers/platform/olpc/
F: drivers/platform/x86/
F: include/linux/platform_data/x86/
X86 PLATFORM DRIVERS - ARCH
R: Darren Hart <dvhart@infradead.org>
R: Andy Shevchenko <andy@infradead.org>
L: platform-driver-x86@vger.kernel.org
L: x86@kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/core
F: arch/x86/platform
X86 PLATFORM UV HPE SUPERDOME FLEX
M: Steve Wahl <steve.wahl@hpe.com>
R: Justin Ernst <justin.ernst@hpe.com>
......
......@@ -1732,6 +1732,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
* Some ACPI devs contain SerialBus resources even though they are not
* attached to a serial bus at all.
*/
{ACPI_VIDEO_HID, },
{"MSHW0028", },
/*
* HIDs of device with an UartSerialBusV2 resource for which userspace
......
......@@ -91,7 +91,7 @@ struct mlxbf_tmfifo_vring {
/* Check whether vring is in drop mode. */
#define IS_VRING_DROP(_r) ({ \
typeof(_r) (r) = (_r); \
(r->desc_head == &r->drop_desc ? true : false); })
r->desc_head == &r->drop_desc; })
/* A stub length to drop maximum length packet. */
#define VRING_DROP_DESC_MAX_LEN GENMASK(15, 0)
......
......@@ -177,10 +177,12 @@ config ACER_WMI
depends on INPUT
depends on RFKILL || RFKILL = n
depends on ACPI_WMI
select ACPI_VIDEO
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on HWMON
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
select ACPI_PLATFORM_PROFILE
help
This is a driver for newer Acer (and Wistron) laptops. It adds
wireless radio and bluetooth control, and on some laptops,
......@@ -1087,6 +1089,21 @@ config INTEL_SCU_IPC_UTIL
source "drivers/platform/x86/siemens/Kconfig"
config SILICOM_PLATFORM
tristate "Silicom Edge Networking device support"
depends on HWMON
depends on GPIOLIB
depends on LEDS_CLASS_MULTICOLOR
help
This option enables support for the LEDs/GPIO/etc downstream of the
embedded controller on Silicom "Cordoba" hardware and derivatives.
This platform driver provides support for various functions via
the Linux LED framework, GPIO framework, Hardware Monitoring (HWMON)
and device attributes.
If you have a Silicom network appliance, say Y or M here.
config WINMATE_FM07_KEYS
tristate "Winmate FM07/FM07P front-panel keys driver"
depends on INPUT
......
......@@ -136,6 +136,9 @@ obj-$(CONFIG_X86_INTEL_LPSS) += pmc_atom.o
# Siemens Simatic Industrial PCs
obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += siemens/
# Silicom
obj-$(CONFIG_SILICOM_PLATFORM) += silicom-platform.o
# Winmate
obj-$(CONFIG_WINMATE_FM07_KEYS) += winmate-fm07-keys.o
......
This diff is collapsed.
......@@ -18,3 +18,17 @@ config AMD_HSMP
If you choose to compile this driver as a module the module will be
called amd_hsmp.
config AMD_WBRF
bool "AMD Wifi RF Band mitigations (WBRF)"
depends on ACPI
help
WBRF(Wifi Band RFI mitigation) mechanism allows Wifi drivers
to notify the frequencies they are using so that other hardware
can be reconfigured to avoid harmonic conflicts.
AMD provides an ACPI based mechanism to support WBRF on platform with
appropriate underlying support.
This mechanism will only be activated on platforms that advertise a
need for it.
......@@ -8,3 +8,4 @@ obj-$(CONFIG_AMD_PMC) += pmc/
amd_hsmp-y := hsmp.o
obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o
obj-$(CONFIG_AMD_PMF) += pmf/
obj-$(CONFIG_AMD_WBRF) += wbrf.o
......@@ -31,13 +31,13 @@
#include "pmc.h"
/* SMU communication registers */
#define AMD_PMC_REGISTER_MESSAGE 0x538
#define AMD_PMC_REGISTER_RESPONSE 0x980
#define AMD_PMC_REGISTER_ARGUMENT 0x9BC
/* PMC Scratch Registers */
#define AMD_PMC_SCRATCH_REG_CZN 0x94
#define AMD_PMC_SCRATCH_REG_YC 0xD14
#define AMD_PMC_SCRATCH_REG_1AH 0xF14
/* STB Registers */
#define AMD_PMC_STB_PMI_0 0x03E30600
......@@ -145,6 +145,7 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = {
{"JPEG", BIT(18)},
{"IPU", BIT(19)},
{"UMSCH", BIT(20)},
{"VPE", BIT(21)},
{}
};
......@@ -350,10 +351,17 @@ static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
case AMD_CPU_ID_CB:
dev->num_ips = 12;
dev->s2d_msg_id = 0xBE;
dev->smu_msg = 0x538;
break;
case AMD_CPU_ID_PS:
dev->num_ips = 21;
dev->s2d_msg_id = 0x85;
dev->smu_msg = 0x538;
break;
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
dev->num_ips = 22;
dev->s2d_msg_id = 0xDE;
dev->smu_msg = 0x938;
break;
}
}
......@@ -588,6 +596,9 @@ static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
case AMD_CPU_ID_PS:
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
break;
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_1AH);
break;
default:
return -EINVAL;
}
......@@ -618,6 +629,7 @@ static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev)
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
case AMD_CPU_ID_PS:
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
return true;
default:
return false;
......@@ -653,7 +665,7 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
argument = AMD_S2D_REGISTER_ARGUMENT;
response = AMD_S2D_REGISTER_RESPONSE;
} else {
message = AMD_PMC_REGISTER_MESSAGE;
message = dev->smu_msg;
argument = AMD_PMC_REGISTER_ARGUMENT;
response = AMD_PMC_REGISTER_RESPONSE;
}
......@@ -680,7 +692,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
argument = AMD_S2D_REGISTER_ARGUMENT;
response = AMD_S2D_REGISTER_RESPONSE;
} else {
message = AMD_PMC_REGISTER_MESSAGE;
message = dev->smu_msg;
argument = AMD_PMC_REGISTER_ARGUMENT;
response = AMD_PMC_REGISTER_RESPONSE;
}
......@@ -751,6 +763,7 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
case AMD_CPU_ID_PS:
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
return MSG_OS_HINT_RN;
}
return -EINVAL;
......@@ -967,9 +980,6 @@ static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)
/* Spill to DRAM feature uses separate SMU message port */
dev->msg_port = 1;
/* Get num of IP blocks within the SoC */
amd_pmc_get_ip_info(dev);
amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true);
if (size != S2D_TELEMETRY_BYTES_MAX)
return -EIO;
......@@ -1077,6 +1087,9 @@ static int amd_pmc_probe(struct platform_device *pdev)
mutex_init(&dev->lock);
/* Get num of IP blocks within the SoC */
amd_pmc_get_ip_info(dev);
if (enable_stb && amd_pmc_is_stb_supported(dev)) {
err = amd_pmc_s2d_init(dev);
if (err)
......
......@@ -26,6 +26,7 @@ struct amd_pmc_dev {
u32 dram_size;
u32 num_ips;
u32 s2d_msg_id;
u32 smu_msg;
/* SMU version information */
u8 smu_program;
u8 major;
......
......@@ -9,6 +9,7 @@ config AMD_PMF
depends on POWER_SUPPLY
depends on AMD_NB
select ACPI_PLATFORM_PROFILE
depends on TEE && AMDTEE
help
This driver provides support for the AMD Platform Management Framework.
The goal is to enhance end user experience by making AMD PCs smarter,
......
......@@ -6,4 +6,5 @@
obj-$(CONFIG_AMD_PMF) += amd-pmf.o
amd-pmf-objs := core.o acpi.o sps.o \
auto-mode.o cnqf.o
auto-mode.o cnqf.o \
tee-if.o spc.o
......@@ -111,7 +111,6 @@ int apmf_os_power_slider_update(struct amd_pmf_dev *pdev, u8 event)
struct os_power_slider args;
struct acpi_buffer params;
union acpi_object *info;
int err = 0;
args.size = sizeof(args);
args.slider_event = event;
......@@ -121,10 +120,10 @@ int apmf_os_power_slider_update(struct amd_pmf_dev *pdev, u8 event)
info = apmf_if_call(pdev, APMF_FUNC_OS_POWER_SLIDER_UPDATE, &params);
if (!info)
err = -EIO;
return -EIO;
kfree(info);
return err;
return 0;
}
static void apmf_sbios_heartbeat_notify(struct work_struct *work)
......@@ -135,11 +134,9 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
if (!info)
goto out;
return;
schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
out:
kfree(info);
}
......@@ -148,7 +145,6 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
union acpi_object *info;
struct apmf_fan_idx args;
struct acpi_buffer params;
int err = 0;
args.size = sizeof(args);
args.fan_ctl_mode = manual;
......@@ -158,14 +154,11 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
params.pointer = (void *)&args;
info = apmf_if_call(pdev, APMF_FUNC_SET_FAN_IDX, &params);
if (!info) {
err = -EIO;
goto out;
}
if (!info)
return -EIO;
out:
kfree(info);
return err;
return 0;
}
int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data)
......@@ -286,6 +279,43 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev)
return 0;
}
static acpi_status apmf_walk_resources(struct acpi_resource *res, void *data)
{
struct amd_pmf_dev *dev = data;
switch (res->type) {
case ACPI_RESOURCE_TYPE_ADDRESS64:
dev->policy_addr = res->data.address64.address.minimum;
dev->policy_sz = res->data.address64.address.address_length;
break;
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
dev->policy_addr = res->data.fixed_memory32.address;
dev->policy_sz = res->data.fixed_memory32.address_length;
break;
}
if (!dev->policy_addr || dev->policy_sz > POLICY_BUF_MAX_SZ || dev->policy_sz == 0) {
pr_err("Incorrect Policy params, possibly a SBIOS bug\n");
return AE_ERROR;
}
return AE_OK;
}
int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev)
{
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
acpi_status status;
status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, apmf_walk_resources, pmf_dev);
if (ACPI_FAILURE(status)) {
dev_err(pmf_dev->dev, "acpi_walk_resources failed :%d\n", status);
return -EINVAL;
}
return 0;
}
void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
{
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
......
......@@ -251,29 +251,37 @@ static const struct pci_device_id pmf_pci_ids[] = {
{ }
};
static void amd_pmf_set_dram_addr(struct amd_pmf_dev *dev)
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
{
u64 phys_addr;
u32 hi, low;
/* Get Metrics Table Address */
if (alloc_buffer) {
dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL);
if (!dev->buf)
return -ENOMEM;
}
phys_addr = virt_to_phys(dev->buf);
hi = phys_addr >> 32;
low = phys_addr & GENMASK(31, 0);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL);
return 0;
}
int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
{
/* Get Metrics Table Address */
dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL);
if (!dev->buf)
return -ENOMEM;
int ret;
INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
amd_pmf_set_dram_addr(dev);
ret = amd_pmf_set_dram_addr(dev, true);
if (ret)
return ret;
/*
* Start collecting the metrics data after a small delay
......@@ -284,17 +292,30 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
return 0;
}
static int amd_pmf_suspend_handler(struct device *dev)
{
struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
kfree(pdev->buf);
return 0;
}
static int amd_pmf_resume_handler(struct device *dev)
{
struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
int ret;
if (pdev->buf)
amd_pmf_set_dram_addr(pdev);
if (pdev->buf) {
ret = amd_pmf_set_dram_addr(pdev, false);
if (ret)
return ret;
}
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, NULL, amd_pmf_resume_handler);
static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, amd_pmf_suspend_handler, amd_pmf_resume_handler);
static void amd_pmf_init_features(struct amd_pmf_dev *dev)
{
......@@ -309,13 +330,13 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev)
dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n");
}
/* Enable Auto Mode */
if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
if (!amd_pmf_init_smart_pc(dev)) {
dev_dbg(dev->dev, "Smart PC Solution Enabled\n");
} else if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
amd_pmf_init_auto_mode(dev);
dev_dbg(dev->dev, "Auto Mode Init done\n");
} else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) ||
is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) {
/* Enable Cool n Quiet Framework (CnQF) */
ret = amd_pmf_init_cnqf(dev);
if (ret)
dev_warn(dev->dev, "CnQF Init failed\n");
......@@ -330,7 +351,9 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
amd_pmf_deinit_sps(dev);
}
if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
if (!dev->smart_pc_enabled) {
amd_pmf_deinit_smart_pc(dev);
} else if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) {
amd_pmf_deinit_auto_mode(dev);
} else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) ||
is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) {
......@@ -408,9 +431,9 @@ static int amd_pmf_probe(struct platform_device *pdev)
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_dbgfs_register(dev);
amd_pmf_init_features(dev);
apmf_install_handler(dev);
amd_pmf_dbgfs_register(dev);
dev_info(dev->dev, "registered PMF device successfully\n");
......@@ -448,3 +471,4 @@ module_platform_driver(amd_pmf_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AMD Platform Management Framework Driver");
MODULE_SOFTDEP("pre: amdtee");
......@@ -14,6 +14,11 @@
#include <linux/acpi.h>
#include <linux/platform_profile.h>
#define POLICY_BUF_MAX_SZ 0x4b000
#define POLICY_SIGN_COOKIE 0x31535024
#define POLICY_COOKIE_OFFSET 0x10
#define POLICY_COOKIE_LEN 0x14
/* APMF Functions */
#define APMF_FUNC_VERIFY_INTERFACE 0
#define APMF_FUNC_GET_SYS_PARAMS 1
......@@ -44,6 +49,7 @@
#define GET_STT_MIN_LIMIT 0x1F
#define GET_STT_LIMIT_APU 0x20
#define GET_STT_LIMIT_HS2 0x21
#define SET_P3T 0x23 /* P3T: Peak Package Power Limit */
/* OS slider update notification */
#define DC_BEST_PERF 0
......@@ -59,6 +65,24 @@
#define ARG_NONE 0
#define AVG_SAMPLE_SIZE 3
/* Policy Actions */
#define PMF_POLICY_SPL 2
#define PMF_POLICY_SPPT 3
#define PMF_POLICY_FPPT 4
#define PMF_POLICY_SPPT_APU_ONLY 5
#define PMF_POLICY_STT_MIN 6
#define PMF_POLICY_STT_SKINTEMP_APU 7
#define PMF_POLICY_STT_SKINTEMP_HS2 8
#define PMF_POLICY_SYSTEM_STATE 9
#define PMF_POLICY_P3T 38
/* TA macros */
#define PMF_TA_IF_VERSION_MAJOR 1
#define TA_PMF_ACTION_MAX 32
#define TA_PMF_UNDO_MAX 8
#define TA_OUTPUT_RESERVED_MEM 906
#define MAX_OPERATION_PARAMS 4
/* AMD PMF BIOS interfaces */
struct apmf_verify_interface {
u16 size;
......@@ -129,6 +153,21 @@ struct smu_pmf_metrics {
u16 infra_gfx_maxfreq; /* in MHz */
u16 skin_temp; /* in centi-Celsius */
u16 device_state;
u16 curtemp; /* in centi-Celsius */
u16 filter_alpha_value;
u16 avg_gfx_clkfrequency;
u16 avg_fclk_frequency;
u16 avg_gfx_activity;
u16 avg_socclk_frequency;
u16 avg_vclk_frequency;
u16 avg_vcn_activity;
u16 avg_dram_reads;
u16 avg_dram_writes;
u16 avg_socket_power;
u16 avg_core_power[2];
u16 avg_core_c0residency[16];
u16 spare1;
u32 metrics_counter;
} __packed;
enum amd_stt_skin_temp {
......@@ -179,6 +218,19 @@ struct amd_pmf_dev {
bool cnqf_enabled;
bool cnqf_supported;
struct notifier_block pwr_src_notifier;
/* Smart PC solution builder */
struct dentry *esbin;
unsigned char *policy_buf;
u32 policy_sz;
struct tee_context *tee_ctx;
struct tee_shm *fw_shm_pool;
u32 session_id;
void *shbuf;
struct delayed_work pb_work;
struct pmf_action_table *prev_data;
u64 policy_addr;
void *policy_base;
bool smart_pc_enabled;
};
struct apmf_sps_prop_granular {
......@@ -389,6 +441,145 @@ struct apmf_dyn_slider_output {
struct apmf_cnqf_power_set ps[APMF_CNQF_MAX];
} __packed;
enum smart_pc_status {
PMF_SMART_PC_ENABLED,
PMF_SMART_PC_DISABLED,
};
/* Smart PC - TA internals */
enum system_state {
SYSTEM_STATE_S0i3,
SYSTEM_STATE_S4,
SYSTEM_STATE_SCREEN_LOCK,
SYSTEM_STATE_MAX,
};
enum ta_slider {
TA_BEST_BATTERY,
TA_BETTER_BATTERY,
TA_BETTER_PERFORMANCE,
TA_BEST_PERFORMANCE,
TA_MAX,
};
/* Command ids for TA communication */
enum ta_pmf_command {
TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE,
TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES,
};
enum ta_pmf_error_type {
TA_PMF_TYPE_SUCCESS,
TA_PMF_ERROR_TYPE_GENERIC,
TA_PMF_ERROR_TYPE_CRYPTO,
TA_PMF_ERROR_TYPE_CRYPTO_VALIDATE,
TA_PMF_ERROR_TYPE_CRYPTO_VERIFY_OEM,
TA_PMF_ERROR_TYPE_POLICY_BUILDER,
TA_PMF_ERROR_TYPE_PB_CONVERT,
TA_PMF_ERROR_TYPE_PB_SETUP,
TA_PMF_ERROR_TYPE_PB_ENACT,
TA_PMF_ERROR_TYPE_ASD_GET_DEVICE_INFO,
TA_PMF_ERROR_TYPE_ASD_GET_DEVICE_PCIE_INFO,
TA_PMF_ERROR_TYPE_SYS_DRV_FW_VALIDATION,
TA_PMF_ERROR_TYPE_MAX,
};
struct pmf_action_table {
enum system_state system_state;
u32 spl; /* in mW */
u32 sppt; /* in mW */
u32 sppt_apuonly; /* in mW */
u32 fppt; /* in mW */
u32 stt_minlimit; /* in mW */
u32 stt_skintemp_apu; /* in C */
u32 stt_skintemp_hs2; /* in C */
u32 p3t_limit; /* in mW */
};
/* Input conditions */
struct ta_pmf_condition_info {
u32 power_source;
u32 bat_percentage;
u32 power_slider;
u32 lid_state;
bool user_present;
u32 rsvd1[2];
u32 monitor_count;
u32 rsvd2[2];
u32 bat_design;
u32 full_charge_capacity;
int drain_rate;
bool user_engaged;
u32 device_state;
u32 socket_power;
u32 skin_temperature;
u32 rsvd3[5];
u32 ambient_light;
u32 length;
u32 avg_c0residency;
u32 max_c0residency;
u32 s0i3_entry;
u32 gfx_busy;
u32 rsvd4[7];
bool camera_state;
u32 workload_type;
u32 display_type;
u32 display_state;
u32 rsvd5[150];
};
struct ta_pmf_load_policy_table {
u32 table_size;
u8 table[POLICY_BUF_MAX_SZ];
};
/* TA initialization params */
struct ta_pmf_init_table {
u32 frequency; /* SMU sampling frequency */
bool validate;
bool sku_check;
bool metadata_macrocheck;
struct ta_pmf_load_policy_table policies_table;
};
/* Everything the TA needs to Enact Policies */
struct ta_pmf_enact_table {
struct ta_pmf_condition_info ev_info;
u32 name;
};
struct ta_pmf_action {
u32 action_index;
u32 value;
};
/* Output actions from TA */
struct ta_pmf_enact_result {
u32 actions_count;
struct ta_pmf_action actions_list[TA_PMF_ACTION_MAX];
u32 undo_count;
struct ta_pmf_action undo_list[TA_PMF_UNDO_MAX];
};
union ta_pmf_input {
struct ta_pmf_enact_table enact_table;
struct ta_pmf_init_table init_table;
};
union ta_pmf_output {
struct ta_pmf_enact_result policy_apply_table;
u32 rsvd[TA_OUTPUT_RESERVED_MEM];
};
struct ta_pmf_shared_memory {
int command_id;
int resp_id;
u32 pmf_result;
u32 if_version;
union ta_pmf_output pmf_output;
union ta_pmf_input pmf_input;
};
/* Core Layer */
int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
......@@ -398,6 +589,7 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev);
int amd_pmf_get_power_source(void);
int apmf_install_handler(struct amd_pmf_dev *pmf_dev);
int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
......@@ -409,7 +601,9 @@ int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output *output);
bool is_pprof_balanced(struct amd_pmf_dev *pmf);
int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev);
const char *amd_pmf_source_as_str(unsigned int state);
const char *amd_pmf_source_as_str(unsigned int state);
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx);
int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf);
......@@ -433,4 +627,13 @@ void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev);
int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms);
extern const struct attribute_group cnqf_feature_attribute_group;
/* Smart PC builder Layer */
int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev);
void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev);
int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev);
/* Smart PC - TA interfaces */
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
#endif /* PMF_H */
// SPDX-License-Identifier: GPL-2.0
/*
* AMD Platform Management Framework Driver - Smart PC Capabilities
*
* Copyright (c) 2023, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
* Patil Rajesh Reddy <Patil.Reddy@amd.com>
*/
#include <acpi/button.h>
#include <linux/power_supply.h>
#include <linux/units.h>
#include "pmf.h"
#ifdef CONFIG_AMD_PMF_DEBUG
static const char *ta_slider_as_str(unsigned int state)
{
switch (state) {
case TA_BEST_PERFORMANCE:
return "PERFORMANCE";
case TA_BETTER_PERFORMANCE:
return "BALANCED";
case TA_BEST_BATTERY:
return "POWER_SAVER";
default:
return "Unknown TA Slider State";
}
}
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
dev_dbg(dev->dev, "==== TA inputs START ====\n");
dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider));
dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source));
dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage);
dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design);
dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity);
dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate);
dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power);
dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature);
dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency);
dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
dev_dbg(dev->dev, "==== TA inputs END ====\n");
}
#else
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
#endif
static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
u16 max, avg = 0;
int i;
memset(dev->buf, 0, sizeof(dev->m_table));
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
in->ev_info.skin_temperature = dev->m_table.skin_temp;
/* Get the avg and max C0 residency of all the cores */
max = dev->m_table.avg_core_c0residency[0];
for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) {
avg += dev->m_table.avg_core_c0residency[i];
if (dev->m_table.avg_core_c0residency[i] > max)
max = dev->m_table.avg_core_c0residency[i];
}
avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency));
in->ev_info.avg_c0residency = avg;
in->ev_info.max_c0residency = max;
in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity;
}
static const char * const pmf_battery_supply_name[] = {
"BATT",
"BAT0",
};
static int amd_pmf_get_battery_prop(enum power_supply_property prop)
{
union power_supply_propval value;
struct power_supply *psy;
int i, ret;
for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) {
psy = power_supply_get_by_name(pmf_battery_supply_name[i]);
if (!psy)
continue;
ret = power_supply_get_property(psy, prop, &value);
if (ret) {
power_supply_put(psy);
return ret;
}
}
return value.intval;
}
static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
int val;
val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT);
if (val < 0)
return val;
if (val != 1)
return -ENODEV;
in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY);
/* all values in mWh metrics */
in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) /
MILLIWATT_PER_WATT;
in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) /
MILLIWATT_PER_WATT;
in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) /
MILLIWATT_PER_WATT;
return 0;
}
static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
int val;
switch (dev->current_profile) {
case PLATFORM_PROFILE_PERFORMANCE:
val = TA_BEST_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
val = TA_BETTER_PERFORMANCE;
break;
case PLATFORM_PROFILE_LOW_POWER:
val = TA_BEST_BATTERY;
break;
default:
dev_err(dev->dev, "Unknown Platform Profile.\n");
return -EOPNOTSUPP;
}
in->ev_info.power_slider = val;
return 0;
}
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
/* TA side lid open is 1 and close is 0, hence the ! here */
in->ev_info.lid_state = !acpi_lid_open();
in->ev_info.power_source = amd_pmf_get_power_source();
amd_pmf_get_smu_info(dev, in);
amd_pmf_get_battery_info(dev, in);
amd_pmf_get_slider_info(dev, in);
}
......@@ -27,7 +27,7 @@ static const char *slider_as_str(unsigned int state)
}
}
static const char *source_as_str(unsigned int state)
const char *amd_pmf_source_as_str(unsigned int state)
{
switch (state) {
case POWER_SOURCE_AC:
......@@ -47,7 +47,8 @@ static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *dat
for (i = 0; i < POWER_SOURCE_MAX; i++) {
for (j = 0; j < POWER_MODE_MAX; j++) {
pr_debug("--- Source:%s Mode:%s ---\n", source_as_str(i), slider_as_str(j));
pr_debug("--- Source:%s Mode:%s ---\n", amd_pmf_source_as_str(i),
slider_as_str(j));
pr_debug("SPL: %u mW\n", data->prop[i][j].spl);
pr_debug("SPPT: %u mW\n", data->prop[i][j].sppt);
pr_debug("SPPT_ApuOnly: %u mW\n", data->prop[i][j].sppt_apu_only);
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Wifi Frequency Band Manage Interface
* Copyright (C) 2023 Advanced Micro Devices
*/
#include <linux/acpi.h>
#include <linux/acpi_amd_wbrf.h>
/*
* Functions bit vector for WBRF method
*
* Bit 0: WBRF supported.
* Bit 1: Function 1 (Add / Remove frequency) is supported.
* Bit 2: Function 2 (Get frequency list) is supported.
*/
#define WBRF_ENABLED 0x0
#define WBRF_RECORD 0x1
#define WBRF_RETRIEVE 0x2
#define WBRF_REVISION 0x1
/*
* The data structure used for WBRF_RETRIEVE is not naturally aligned.
* And unfortunately the design has been settled down.
*/
struct amd_wbrf_ranges_out {
u32 num_of_ranges;
struct freq_band_range band_list[MAX_NUM_OF_WBRF_RANGES];
} __packed;
static const guid_t wifi_acpi_dsm_guid =
GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
/*
* Used to notify consumer (amdgpu driver currently) about
* the wifi frequency is change.
*/
static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ranges_in_out *in)
{
union acpi_object argv4;
union acpi_object *tmp;
union acpi_object *obj;
u32 num_of_ranges = 0;
u32 num_of_elements;
u32 arg_idx = 0;
int ret;
u32 i;
if (!in)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(in->band_list); i++) {
if (in->band_list[i].start && in->band_list[i].end)
num_of_ranges++;
}
/*
* The num_of_ranges value in the "in" object supplied by
* the caller is required to be equal to the number of
* entries in the band_list array in there.
*/
if (num_of_ranges != in->num_of_ranges)
return -EINVAL;
/*
* Every input frequency band comes with two end points(start/end)
* and each is accounted as an element. Meanwhile the range count
* and action type are accounted as an element each.
* So, the total element count = 2 * num_of_ranges + 1 + 1.
*/
num_of_elements = 2 * num_of_ranges + 2;
tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
argv4.package.type = ACPI_TYPE_PACKAGE;
argv4.package.count = num_of_elements;
argv4.package.elements = tmp;
/* save the number of ranges*/
tmp[0].integer.type = ACPI_TYPE_INTEGER;
tmp[0].integer.value = num_of_ranges;
/* save the action(WBRF_RECORD_ADD/REMOVE/RETRIEVE) */
tmp[1].integer.type = ACPI_TYPE_INTEGER;
tmp[1].integer.value = action;
arg_idx = 2;
for (i = 0; i < ARRAY_SIZE(in->band_list); i++) {
if (!in->band_list[i].start || !in->band_list[i].end)
continue;
tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
tmp[arg_idx++].integer.value = in->band_list[i].start;
tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
tmp[arg_idx++].integer.value = in->band_list[i].end;
}
obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
WBRF_REVISION, WBRF_RECORD, &argv4);
if (!obj)
return -EINVAL;
if (obj->type != ACPI_TYPE_INTEGER) {
ret = -EINVAL;
goto out;
}
ret = obj->integer.value;
if (ret)
ret = -EINVAL;
out:
ACPI_FREE(obj);
kfree(tmp);
return ret;
}
/**
* acpi_amd_wbrf_add_remove - add or remove the frequency band the device is using
*
* @dev: device pointer
* @action: remove or add the frequency band into bios
* @in: input structure containing the frequency band the device is using
*
* Broadcast to other consumers the frequency band the device starts
* to use. Underneath the surface the information is cached into an
* internal buffer first. Then a notification is sent to all those
* registered consumers. So then they can retrieve that buffer to
* know the latest active frequency bands. Consumers that haven't
* yet been registered can retrieve the information from the cache
* when they register.
*
* Return:
* 0 for success add/remove wifi frequency band.
* Returns a negative error code for failure.
*/
int acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action, struct wbrf_ranges_in_out *in)
{
struct acpi_device *adev;
int ret;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
ret = wbrf_record(adev, action, in);
if (ret)
return ret;
blocking_notifier_call_chain(&wbrf_chain_head, WBRF_CHANGED, NULL);
return 0;
}
EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_remove);
/**
* acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
* for the device as a producer
*
* @dev: device pointer
*
* Check if the platform equipped with necessary implementations to
* support WBRF for the device as a producer.
*
* Return:
* true if WBRF is supported, otherwise returns false
*/
bool acpi_amd_wbrf_supported_producer(struct device *dev)
{
struct acpi_device *adev;
adev = ACPI_COMPANION(dev);
if (!adev)
return false;
return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
WBRF_REVISION, BIT(WBRF_RECORD));
}
EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_producer);
/**
* acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
* for the device as a consumer
*
* @dev: device pointer
*
* Determine if the platform equipped with necessary implementations to
* support WBRF for the device as a consumer.
*
* Return:
* true if WBRF is supported, otherwise returns false.
*/
bool acpi_amd_wbrf_supported_consumer(struct device *dev)
{
struct acpi_device *adev;
adev = ACPI_COMPANION(dev);
if (!adev)
return false;
return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
WBRF_REVISION, BIT(WBRF_RETRIEVE));
}
EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
/**
* amd_wbrf_retrieve_freq_band - retrieve current active frequency bands
*
* @dev: device pointer
* @out: output structure containing all the active frequency bands
*
* Retrieve the current active frequency bands which were broadcasted
* by other producers. The consumer who calls this API should take
* proper actions if any of the frequency band may cause RFI with its
* own frequency band used.
*
* Return:
* 0 for getting wifi freq band successfully.
* Returns a negative error code for failure.
*/
int amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out)
{
struct amd_wbrf_ranges_out acpi_out = {0};
struct acpi_device *adev;
union acpi_object *obj;
union acpi_object param;
int ret = 0;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
param.type = ACPI_TYPE_STRING;
param.string.length = 0;
param.string.pointer = NULL;
obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
WBRF_REVISION, WBRF_RETRIEVE, &param);
if (!obj)
return -EINVAL;
/*
* The return buffer is with variable length and the format below:
* number_of_entries(1 DWORD): Number of entries
* start_freq of 1st entry(1 QWORD): Start frequency of the 1st entry
* end_freq of 1st entry(1 QWORD): End frequency of the 1st entry
* ...
* ...
* start_freq of the last entry(1 QWORD)
* end_freq of the last entry(1 QWORD)
*
* Thus the buffer length is determined by the number of entries.
* - For zero entry scenario, the buffer length will be 4 bytes.
* - For one entry scenario, the buffer length will be 20 bytes.
*/
if (obj->buffer.length > sizeof(acpi_out) || obj->buffer.length < 4) {
dev_err(dev, "Wrong sized WBRT information");
ret = -EINVAL;
goto out;
}
memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
out->num_of_ranges = acpi_out.num_of_ranges;
memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));
out:
ACPI_FREE(obj);
return ret;
}
EXPORT_SYMBOL_GPL(amd_wbrf_retrieve_freq_band);
/**
* amd_wbrf_register_notifier - register for notifications of frequency
* band update
*
* @nb: driver notifier block
*
* The consumer should register itself via this API so that it can get
* notified on the frequency band updates from other producers.
*
* Return:
* 0 for registering a consumer driver successfully.
* Returns a negative error code for failure.
*/
int amd_wbrf_register_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&wbrf_chain_head, nb);
}
EXPORT_SYMBOL_GPL(amd_wbrf_register_notifier);
/**
* amd_wbrf_unregister_notifier - unregister for notifications of
* frequency band update
*
* @nb: driver notifier block
*
* The consumer should call this API when it is longer interested with
* the frequency band updates from other producers. Usually, this should
* be performed during driver cleanup.
*
* Return:
* 0 for unregistering a consumer driver.
* Returns a negative error code for failure.
*/
int amd_wbrf_unregister_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
}
EXPORT_SYMBOL_GPL(amd_wbrf_unregister_notifier);
......@@ -1816,9 +1816,8 @@ static void asus_dmi_check(void)
return;
/* On L1400B WLED control the sound card, don't mess with it ... */
if (strncmp(model, "L1400B", 6) == 0) {
if (strncmp(model, "L1400B", 6) == 0)
wlan_status = -1;
}
}
static bool asus_device_present;
......
......@@ -4615,7 +4615,7 @@ static int asus_wmi_add(struct platform_device *pdev)
return err;
}
static int asus_wmi_remove(struct platform_device *device)
static void asus_wmi_remove(struct platform_device *device)
{
struct asus_wmi *asus;
......@@ -4638,7 +4638,6 @@ static int asus_wmi_remove(struct platform_device *device)
platform_profile_remove();
kfree(asus);
return 0;
}
/* Platform driver - hibernate/resume callbacks *******************************/
......@@ -4799,7 +4798,7 @@ int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
return -EBUSY;
platform_driver = &driver->platform_driver;
platform_driver->remove = asus_wmi_remove;
platform_driver->remove_new = asus_wmi_remove;
platform_driver->driver.owner = driver->owner;
platform_driver->driver.name = driver->name;
platform_driver->driver.pm = &asus_pm_ops;
......
......@@ -429,7 +429,6 @@ static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
static int alienware_zone_init(struct platform_device *dev)
{
u8 zone;
char buffer[10];
char *name;
if (interface == WMAX) {
......@@ -466,8 +465,7 @@ static int alienware_zone_init(struct platform_device *dev)
return -ENOMEM;
for (zone = 0; zone < quirks->num_zones; zone++) {
sprintf(buffer, "zone%02hhX", zone);
name = kstrdup(buffer, GFP_KERNEL);
name = kasprintf(GFP_KERNEL, "zone%02hhX", zone);
if (name == NULL)
return 1;
sysfs_attr_init(&zone_dev_attrs[zone].attr);
......
......@@ -6,12 +6,16 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/wmi.h>
#include <uapi/linux/wmi.h>
#include "dell-smbios.h"
#include "dell-wmi-descriptor.h"
......@@ -32,7 +36,8 @@ struct wmi_smbios_priv {
struct list_head list;
struct wmi_device *wdev;
struct device *child;
u32 req_buf_size;
u64 req_buf_size;
struct miscdevice char_dev;
};
static LIST_HEAD(wmi_list);
......@@ -108,48 +113,115 @@ static int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
return ret;
}
static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd,
struct wmi_ioctl_buffer *arg)
static int dell_smbios_wmi_open(struct inode *inode, struct file *filp)
{
struct wmi_smbios_priv *priv;
int ret = 0;
switch (cmd) {
case DELL_WMI_SMBIOS_CMD:
mutex_lock(&call_mutex);
priv = dev_get_drvdata(&wdev->dev);
if (!priv) {
ret = -ENODEV;
goto fail_smbios_cmd;
}
memcpy(priv->buf, arg, priv->req_buf_size);
if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) {
dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n",
priv->buf->std.cmd_class,
priv->buf->std.cmd_select,
priv->buf->std.input[0]);
ret = -EFAULT;
goto fail_smbios_cmd;
}
ret = run_smbios_call(priv->wdev);
if (ret)
goto fail_smbios_cmd;
memcpy(arg, priv->buf, priv->req_buf_size);
fail_smbios_cmd:
mutex_unlock(&call_mutex);
break;
default:
ret = -ENOIOCTLCMD;
priv = container_of(filp->private_data, struct wmi_smbios_priv, char_dev);
filp->private_data = priv;
return nonseekable_open(inode, filp);
}
static ssize_t dell_smbios_wmi_read(struct file *filp, char __user *buffer, size_t length,
loff_t *offset)
{
struct wmi_smbios_priv *priv = filp->private_data;
return simple_read_from_buffer(buffer, length, offset, &priv->req_buf_size,
sizeof(priv->req_buf_size));
}
static long dell_smbios_wmi_do_ioctl(struct wmi_smbios_priv *priv,
struct dell_wmi_smbios_buffer __user *arg)
{
long ret;
if (get_user(priv->buf->length, &arg->length))
return -EFAULT;
if (priv->buf->length < priv->req_buf_size)
return -EINVAL;
/* if it's too big, warn, driver will only use what is needed */
if (priv->buf->length > priv->req_buf_size)
dev_err(&priv->wdev->dev, "Buffer %llu is bigger than required %llu\n",
priv->buf->length, priv->req_buf_size);
if (copy_from_user(priv->buf, arg, priv->req_buf_size))
return -EFAULT;
if (dell_smbios_call_filter(&priv->wdev->dev, &priv->buf->std)) {
dev_err(&priv->wdev->dev, "Invalid call %d/%d:%8x\n",
priv->buf->std.cmd_class,
priv->buf->std.cmd_select,
priv->buf->std.input[0]);
return -EINVAL;
}
ret = run_smbios_call(priv->wdev);
if (ret)
return ret;
if (copy_to_user(arg, priv->buf, priv->req_buf_size))
return -EFAULT;
return 0;
}
static long dell_smbios_wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct dell_wmi_smbios_buffer __user *input = (struct dell_wmi_smbios_buffer __user *)arg;
struct wmi_smbios_priv *priv = filp->private_data;
long ret;
if (cmd != DELL_WMI_SMBIOS_CMD)
return -ENOIOCTLCMD;
mutex_lock(&call_mutex);
ret = dell_smbios_wmi_do_ioctl(priv, input);
mutex_unlock(&call_mutex);
return ret;
}
static const struct file_operations dell_smbios_wmi_fops = {
.owner = THIS_MODULE,
.open = dell_smbios_wmi_open,
.read = dell_smbios_wmi_read,
.unlocked_ioctl = dell_smbios_wmi_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
static void dell_smbios_wmi_unregister_chardev(void *data)
{
struct miscdevice *char_dev = data;
misc_deregister(char_dev);
}
static int dell_smbios_wmi_register_chardev(struct wmi_smbios_priv *priv)
{
int ret;
priv->char_dev.minor = MISC_DYNAMIC_MINOR;
priv->char_dev.name = "wmi/dell-smbios";
priv->char_dev.fops = &dell_smbios_wmi_fops;
priv->char_dev.mode = 0444;
ret = misc_register(&priv->char_dev);
if (ret < 0)
return ret;
return devm_add_action_or_reset(&priv->wdev->dev, dell_smbios_wmi_unregister_chardev,
&priv->char_dev);
}
static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct wmi_driver *wdriver =
container_of(wdev->dev.driver, struct wmi_driver, driver);
struct wmi_smbios_priv *priv;
u32 hotfix;
u32 buffer_size, hotfix;
int count;
int ret;
......@@ -162,62 +234,58 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context)
if (!priv)
return -ENOMEM;
priv->wdev = wdev;
dev_set_drvdata(&wdev->dev, priv);
/* WMI buffer size will be either 4k or 32k depending on machine */
if (!dell_wmi_get_size(&priv->req_buf_size))
if (!dell_wmi_get_size(&buffer_size))
return -EPROBE_DEFER;
priv->req_buf_size = buffer_size;
/* some SMBIOS calls fail unless BIOS contains hotfix */
if (!dell_wmi_get_hotfix(&hotfix))
return -EPROBE_DEFER;
if (!hotfix) {
if (!hotfix)
dev_warn(&wdev->dev,
"WMI SMBIOS userspace interface not supported(%u), try upgrading to a newer BIOS\n",
hotfix);
wdriver->filter_callback = NULL;
}
/* add in the length object we will use internally with ioctl */
priv->req_buf_size += sizeof(u64);
ret = set_required_buffer_size(wdev, priv->req_buf_size);
if (ret)
return ret;
count = get_order(priv->req_buf_size);
priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
priv->buf = (void *)devm_get_free_pages(&wdev->dev, GFP_KERNEL, count);
if (!priv->buf)
return -ENOMEM;
ret = dell_smbios_wmi_register_chardev(priv);
if (ret)
return ret;
/* ID is used by dell-smbios to set priority of drivers */
wdev->dev.id = 1;
ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call);
if (ret)
goto fail_register;
return ret;
priv->wdev = wdev;
dev_set_drvdata(&wdev->dev, priv);
mutex_lock(&list_mutex);
list_add_tail(&priv->list, &wmi_list);
mutex_unlock(&list_mutex);
return 0;
fail_register:
free_pages((unsigned long)priv->buf, count);
return ret;
}
static void dell_smbios_wmi_remove(struct wmi_device *wdev)
{
struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
int count;
mutex_lock(&call_mutex);
mutex_lock(&list_mutex);
list_del(&priv->list);
mutex_unlock(&list_mutex);
dell_smbios_unregister_device(&wdev->dev);
count = get_order(priv->req_buf_size);
free_pages((unsigned long)priv->buf, count);
mutex_unlock(&call_mutex);
}
......@@ -256,7 +324,6 @@ static struct wmi_driver dell_smbios_wmi_driver = {
.probe = dell_smbios_wmi_probe,
.remove = dell_smbios_wmi_remove,
.id_table = dell_smbios_wmi_id_table,
.filter_callback = dell_smbios_wmi_filter,
};
int init_dell_smbios_wmi(void)
......
......@@ -7,7 +7,6 @@
*/
#include "bioscfg.h"
#include <asm-generic/posix_types.h>
GET_INSTANCE_ID(password);
/*
......
......@@ -1478,7 +1478,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
return 0;
}
static int __exit hp_wmi_bios_remove(struct platform_device *device)
static void __exit hp_wmi_bios_remove(struct platform_device *device)
{
int i;
......@@ -1502,8 +1502,6 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
if (platform_profile_support)
platform_profile_remove();
return 0;
}
static int hp_wmi_resume_handler(struct device *device)
......@@ -1560,7 +1558,7 @@ static struct platform_driver hp_wmi_driver __refdata = {
.pm = &hp_wmi_pm_ops,
.dev_groups = hp_wmi_groups,
},
.remove = __exit_p(hp_wmi_bios_remove),
.remove_new = __exit_p(hp_wmi_bios_remove),
};
static umode_t hp_wmi_hwmon_is_visible(const void *data,
......
......@@ -7,6 +7,7 @@ config INTEL_PMC_CORE
tristate "Intel PMC Core driver"
depends on PCI
depends on ACPI
depends on INTEL_PMT_TELEMETRY
help
The Intel Platform Controller Hub for Intel Core SoCs provides access
to Power Management Controller registers via various interfaces. This
......
......@@ -4,7 +4,7 @@
#
intel_pmc_core-y := core.o core_ssram.o spt.o cnp.o \
icl.o tgl.o adl.o mtl.o
icl.o tgl.o adl.o mtl.o arl.o lnl.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
intel_pmc_core_pltdrv-y := pltdrv.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o
......@@ -307,6 +307,8 @@ const struct pmc_reg_map adl_reg_map = {
.lpm_sts = adl_lpm_maps,
.lpm_status_offset = ADL_LPM_STATUS_OFFSET,
.lpm_live_status_offset = ADL_LPM_LIVE_STATUS_OFFSET,
.pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET,
.pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP,
};
int adl_core_init(struct pmc_dev *pmcdev)
......@@ -322,5 +324,7 @@ int adl_core_init(struct pmc_dev *pmcdev)
if (ret)
return ret;
pmc_core_get_low_power_modes(pmcdev);
return 0;
}
This diff is collapsed.
......@@ -234,5 +234,7 @@ int cnp_core_init(struct pmc_dev *pmcdev)
if (ret)
return ret;
pmc_core_get_low_power_modes(pmcdev);
return 0;
}
This diff is collapsed.
......@@ -16,6 +16,8 @@
#include <linux/bits.h>
#include <linux/platform_device.h>
struct telem_endpoint;
#define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0)
#define PMC_BASE_ADDR_DEFAULT 0xFE000000
......@@ -221,6 +223,10 @@ enum ppfear_regs {
#define TGL_LPM_PRI_OFFSET 0x1C7C
#define TGL_LPM_NUM_MAPS 6
/* Tigerlake PSON residency register */
#define TGL_PSON_RESIDENCY_OFFSET 0x18f8
#define TGL_PSON_RES_COUNTER_STEP 0x7A
/* Extended Test Mode Register 3 (CNL and later) */
#define ETR3_OFFSET 0x1048
#define ETR3_CF9GR BIT(20)
......@@ -257,10 +263,25 @@ enum ppfear_regs {
#define MTL_SOCM_NUM_IP_IGN_ALLOWED 25
#define MTL_SOC_PMC_MMIO_REG_LEN 0x2708
#define MTL_PMC_LTR_SPG 0x1B74
#define ARL_SOCS_PMC_LTR_RESERVED 0x1B88
#define ARL_SOCS_NUM_IP_IGN_ALLOWED 26
#define ARL_PMC_LTR_DMI3 0x1BE4
#define ARL_PCH_PMC_MMIO_REG_LEN 0x2720
/* Meteor Lake PGD PFET Enable Ack Status */
#define MTL_SOCM_PPFEAR_NUM_ENTRIES 8
#define MTL_IOE_PPFEAR_NUM_ENTRIES 10
#define ARL_SOCS_PPFEAR_NUM_ENTRIES 9
/* Die C6 from PUNIT telemetry */
#define MTL_PMT_DMU_DIE_C6_OFFSET 15
#define MTL_PMT_DMU_GUID 0x1A067102
#define ARL_PMT_DMU_GUID 0x1A06A000
#define LNL_PMC_MMIO_REG_LEN 0x2708
#define LNL_PMC_LTR_OSSE 0x1B88
#define LNL_NUM_IP_IGN_ALLOWED 27
#define LNL_PPFEAR_NUM_ENTRIES 12
extern const char *pmc_lpm_modes[];
......@@ -320,6 +341,9 @@ struct pmc_reg_map {
const u32 lpm_status_offset;
const u32 lpm_live_status_offset;
const u32 etr3_offset;
const u8 *lpm_reg_index;
const u32 pson_residency_offset;
const u32 pson_residency_counter_step;
};
/**
......@@ -329,6 +353,7 @@ struct pmc_reg_map {
* specific attributes
*/
struct pmc_info {
u32 guid;
u16 devid;
const struct pmc_reg_map *map;
};
......@@ -355,6 +380,7 @@ struct pmc {
* @devs: pointer to an array of pmc pointers
* @pdev: pointer to platform_device struct
* @ssram_pcidev: pointer to pci device struct for the PMC SSRAM
* @crystal_freq: crystal frequency from cpuid
* @dbgfs_dir: path to debugfs interface
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available
......@@ -373,6 +399,7 @@ struct pmc_dev {
struct dentry *dbgfs_dir;
struct platform_device *pdev;
struct pci_dev *ssram_pcidev;
unsigned int crystal_freq;
int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */
......@@ -425,6 +452,7 @@ extern const struct pmc_bit_map tgl_vnn_misc_status_map[];
extern const struct pmc_bit_map tgl_signal_status_map[];
extern const struct pmc_bit_map *tgl_lpm_maps[];
extern const struct pmc_reg_map tgl_reg_map;
extern const struct pmc_reg_map tgl_h_reg_map;
extern const struct pmc_bit_map adl_pfear_map[];
extern const struct pmc_bit_map *ext_adl_pfear_map[];
extern const struct pmc_bit_map adl_ltr_show_map[];
......@@ -486,21 +514,80 @@ extern const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[];
extern const struct pmc_bit_map mtl_ioem_vnn_req_status_1_map[];
extern const struct pmc_bit_map *mtl_ioem_lpm_maps[];
extern const struct pmc_reg_map mtl_ioem_reg_map;
extern const struct pmc_reg_map lnl_socm_reg_map;
/* LNL */
extern const struct pmc_bit_map lnl_ltr_show_map[];
extern const struct pmc_bit_map lnl_clocksource_status_map[];
extern const struct pmc_bit_map lnl_power_gating_status_0_map[];
extern const struct pmc_bit_map lnl_power_gating_status_1_map[];
extern const struct pmc_bit_map lnl_power_gating_status_2_map[];
extern const struct pmc_bit_map lnl_d3_status_0_map[];
extern const struct pmc_bit_map lnl_d3_status_1_map[];
extern const struct pmc_bit_map lnl_d3_status_2_map[];
extern const struct pmc_bit_map lnl_d3_status_3_map[];
extern const struct pmc_bit_map lnl_vnn_req_status_0_map[];
extern const struct pmc_bit_map lnl_vnn_req_status_1_map[];
extern const struct pmc_bit_map lnl_vnn_req_status_2_map[];
extern const struct pmc_bit_map lnl_vnn_req_status_3_map[];
extern const struct pmc_bit_map lnl_vnn_misc_status_map[];
extern const struct pmc_bit_map *lnl_lpm_maps[];
extern const struct pmc_bit_map lnl_pfear_map[];
extern const struct pmc_bit_map *ext_lnl_pfear_map[];
/* ARL */
extern const struct pmc_bit_map arl_socs_ltr_show_map[];
extern const struct pmc_bit_map arl_socs_clocksource_status_map[];
extern const struct pmc_bit_map arl_socs_power_gating_status_0_map[];
extern const struct pmc_bit_map arl_socs_power_gating_status_1_map[];
extern const struct pmc_bit_map arl_socs_power_gating_status_2_map[];
extern const struct pmc_bit_map arl_socs_d3_status_2_map[];
extern const struct pmc_bit_map arl_socs_d3_status_3_map[];
extern const struct pmc_bit_map arl_socs_vnn_req_status_3_map[];
extern const struct pmc_bit_map *arl_socs_lpm_maps[];
extern const struct pmc_bit_map arl_socs_pfear_map[];
extern const struct pmc_bit_map *ext_arl_socs_pfear_map[];
extern const struct pmc_reg_map arl_socs_reg_map;
extern const struct pmc_bit_map arl_pchs_ltr_show_map[];
extern const struct pmc_bit_map arl_pchs_clocksource_status_map[];
extern const struct pmc_bit_map arl_pchs_power_gating_status_0_map[];
extern const struct pmc_bit_map arl_pchs_power_gating_status_1_map[];
extern const struct pmc_bit_map arl_pchs_power_gating_status_2_map[];
extern const struct pmc_bit_map arl_pchs_d3_status_0_map[];
extern const struct pmc_bit_map arl_pchs_d3_status_1_map[];
extern const struct pmc_bit_map arl_pchs_d3_status_2_map[];
extern const struct pmc_bit_map arl_pchs_d3_status_3_map[];
extern const struct pmc_bit_map arl_pchs_vnn_req_status_0_map[];
extern const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[];
extern const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[];
extern const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[];
extern const struct pmc_bit_map arl_pchs_vnn_misc_status_map[];
extern const struct pmc_bit_map arl_pchs_signal_status_map[];
extern const struct pmc_bit_map *arl_pchs_lpm_maps[];
extern const struct pmc_reg_map arl_pchs_reg_map;
extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
extern int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev);
int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
int pmc_core_resume_common(struct pmc_dev *pmcdev);
int get_primary_reg_base(struct pmc *pmc);
extern void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev);
extern void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid);
extern void pmc_core_set_device_d3(unsigned int device);
extern void pmc_core_ssram_init(struct pmc_dev *pmcdev);
extern int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func);
int spt_core_init(struct pmc_dev *pmcdev);
int cnp_core_init(struct pmc_dev *pmcdev);
int icl_core_init(struct pmc_dev *pmcdev);
int tgl_core_init(struct pmc_dev *pmcdev);
int tgl_l_core_init(struct pmc_dev *pmcdev);
int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp);
int adl_core_init(struct pmc_dev *pmcdev);
int mtl_core_init(struct pmc_dev *pmcdev);
int arl_core_init(struct pmc_dev *pmcdev);
int lnl_core_init(struct pmc_dev *pmcdev);
void cnl_suspend(struct pmc_dev *pmcdev);
int cnl_resume(struct pmc_dev *pmcdev);
......
......@@ -8,10 +8,13 @@
*
*/
#include <linux/cleanup.h>
#include <linux/pci.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include "core.h"
#include "../vsec.h"
#include "../pmt/telemetry.h"
#define SSRAM_HDR_SIZE 0x100
#define SSRAM_PWRM_OFFSET 0x14
......@@ -21,6 +24,185 @@
#define SSRAM_IOE_OFFSET 0x68
#define SSRAM_DEVID_OFFSET 0x70
/* PCH query */
#define LPM_HEADER_OFFSET 1
#define LPM_REG_COUNT 28
#define LPM_MODE_OFFSET 1
DEFINE_FREE(pmc_core_iounmap, void __iomem *, iounmap(_T));
static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map)
{
for (; list->map; ++list)
if (list->map == map)
return list->guid;
return 0;
}
static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc)
{
struct telem_endpoint *ep;
const u8 *lpm_indices;
int num_maps, mode_offset = 0;
int ret, mode, i;
int lpm_size;
u32 guid;
lpm_indices = pmc->map->lpm_reg_index;
num_maps = pmc->map->lpm_num_maps;
lpm_size = LPM_MAX_NUM_MODES * num_maps;
guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map);
if (!guid)
return -ENXIO;
ep = pmt_telem_find_and_register_endpoint(pmcdev->ssram_pcidev, guid, 0);
if (IS_ERR(ep)) {
dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %ld",
PTR_ERR(ep));
return -EPROBE_DEFER;
}
pmc->lpm_req_regs = devm_kzalloc(&pmcdev->pdev->dev,
lpm_size * sizeof(u32),
GFP_KERNEL);
if (!pmc->lpm_req_regs) {
ret = -ENOMEM;
goto unregister_ep;
}
/*
* PMC Low Power Mode (LPM) table
*
* In telemetry space, the LPM table contains a 4 byte header followed
* by 8 consecutive mode blocks (one for each LPM mode). Each block
* has a 4 byte header followed by a set of registers that describe the
* IP state requirements for the given mode. The IP mapping is platform
* specific but the same for each block, making for easy analysis.
* Platforms only use a subset of the space to track the requirements
* for their IPs. Callers provide the requirement registers they use as
* a list of indices. Each requirement register is associated with an
* IP map that's maintained by the caller.
*
* Header
* +----+----------------------------+----------------------------+
* | 0 | REVISION | ENABLED MODES |
* +----+--------------+-------------+-------------+--------------+
*
* Low Power Mode 0 Block
* +----+--------------+-------------+-------------+--------------+
* | 1 | SUB ID | SIZE | MAJOR | MINOR |
* +----+--------------+-------------+-------------+--------------+
* | 2 | LPM0 Requirements 0 |
* +----+---------------------------------------------------------+
* | | ... |
* +----+---------------------------------------------------------+
* | 29 | LPM0 Requirements 27 |
* +----+---------------------------------------------------------+
*
* ...
*
* Low Power Mode 7 Block
* +----+--------------+-------------+-------------+--------------+
* | | SUB ID | SIZE | MAJOR | MINOR |
* +----+--------------+-------------+-------------+--------------+
* | 60 | LPM7 Requirements 0 |
* +----+---------------------------------------------------------+
* | | ... |
* +----+---------------------------------------------------------+
* | 87 | LPM7 Requirements 27 |
* +----+---------------------------------------------------------+
*
*/
mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET;
pmc_for_each_mode(i, mode, pmcdev) {
u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps);
int m;
for (m = 0; m < num_maps; m++) {
u8 sample_id = lpm_indices[m] + mode_offset;
ret = pmt_telem_read32(ep, sample_id, req_offset, 1);
if (ret) {
dev_err(&pmcdev->pdev->dev,
"couldn't read Low Power Mode requirements: %d\n", ret);
devm_kfree(&pmcdev->pdev->dev, pmc->lpm_req_regs);
goto unregister_ep;
}
++req_offset;
}
mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET;
}
unregister_ep:
pmt_telem_unregister_endpoint(ep);
return ret;
}
int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev)
{
int ret, i;
if (!pmcdev->ssram_pcidev)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) {
if (!pmcdev->pmcs[i])
continue;
ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i]);
if (ret)
return ret;
}
return 0;
}
static void
pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram)
{
struct pci_dev *pcidev = pmcdev->ssram_pcidev;
struct intel_vsec_platform_info info = {};
struct intel_vsec_header *headers[2] = {};
struct intel_vsec_header header;
void __iomem *dvsec;
u32 dvsec_offset;
u32 table, hdr;
ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
if (!ssram)
return;
dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET);
iounmap(ssram);
dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE);
if (!dvsec)
return;
hdr = readl(dvsec + PCI_DVSEC_HEADER1);
header.id = readw(dvsec + PCI_DVSEC_HEADER2);
header.rev = PCI_DVSEC_HEADER1_REV(hdr);
header.length = PCI_DVSEC_HEADER1_LEN(hdr);
header.num_entries = readb(dvsec + INTEL_DVSEC_ENTRIES);
header.entry_size = readb(dvsec + INTEL_DVSEC_SIZE);
table = readl(dvsec + INTEL_DVSEC_TABLE);
header.tbir = INTEL_DVSEC_TABLE_BAR(table);
header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
iounmap(dvsec);
headers[0] = &header;
info.caps = VSEC_CAP_TELEMETRY;
info.headers = headers;
info.base_addr = ssram_base;
info.parent = &pmcdev->pdev->dev;
intel_vsec_register(pcidev, &info);
}
static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid)
{
for (; list->map; ++list)
......@@ -35,20 +217,20 @@ static inline u64 get_base(void __iomem *addr, u32 offset)
return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3);
}
static void
static int
pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base,
const struct pmc_reg_map *reg_map, int pmc_index)
{
struct pmc *pmc = pmcdev->pmcs[pmc_index];
if (!pwrm_base)
return;
return -ENODEV;
/* Memory for primary PMC has been allocated in core.c */
if (!pmc) {
pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL);
if (!pmc)
return;
return -ENOMEM;
}
pmc->map = reg_map;
......@@ -57,77 +239,88 @@ pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base,
if (!pmc->regbase) {
devm_kfree(&pmcdev->pdev->dev, pmc);
return;
return -ENOMEM;
}
pmcdev->pmcs[pmc_index] = pmc;
return 0;
}
static void
pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, void __iomem *ssram, u32 offset,
int pmc_idx)
static int
pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, int pmc_idx, u32 offset)
{
u64 pwrm_base;
struct pci_dev *ssram_pcidev = pmcdev->ssram_pcidev;
void __iomem __free(pmc_core_iounmap) *tmp_ssram = NULL;
void __iomem __free(pmc_core_iounmap) *ssram = NULL;
const struct pmc_reg_map *map;
u64 ssram_base, pwrm_base;
u16 devid;
if (pmc_idx != PMC_IDX_SOC) {
u64 ssram_base = get_base(ssram, offset);
if (!pmcdev->regmap_list)
return -ENOENT;
if (!ssram_base)
return;
ssram_base = ssram_pcidev->resource[0].start;
tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
if (pmc_idx != PMC_IDX_MAIN) {
/*
* The secondary PMC BARS (which are behind hidden PCI devices)
* are read from fixed offsets in MMIO of the primary PMC BAR.
*/
ssram_base = get_base(tmp_ssram, offset);
ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
if (!ssram)
return;
return -ENOMEM;
} else {
ssram = no_free_ptr(tmp_ssram);
}
pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
devid = readw(ssram + SSRAM_DEVID_OFFSET);
if (pmcdev->regmap_list) {
const struct pmc_reg_map *map;
/* Find and register and PMC telemetry entries */
pmc_add_pmt(pmcdev, ssram_base, ssram);
map = pmc_core_find_regmap(pmcdev->regmap_list, devid);
if (map)
pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx);
}
map = pmc_core_find_regmap(pmcdev->regmap_list, devid);
if (!map)
return -ENODEV;
if (pmc_idx != PMC_IDX_SOC)
iounmap(ssram);
return pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx);
}
void pmc_core_ssram_init(struct pmc_dev *pmcdev)
int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func)
{
void __iomem *ssram;
struct pci_dev *pcidev;
u64 ssram_base;
int ret;
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2));
pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func));
if (!pcidev)
goto out;
return -ENODEV;
ret = pcim_enable_device(pcidev);
if (ret)
goto release_dev;
ssram_base = pcidev->resource[0].start;
ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
if (!ssram)
goto disable_dev;
pmcdev->ssram_pcidev = pcidev;
pmc_core_ssram_get_pmc(pmcdev, ssram, 0, PMC_IDX_SOC);
pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_IOE_OFFSET, PMC_IDX_IOE);
pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_PCH_OFFSET, PMC_IDX_PCH);
ret = pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_MAIN, 0);
if (ret)
goto disable_dev;
iounmap(ssram);
out:
return;
pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_IOE, SSRAM_IOE_OFFSET);
pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_PCH, SSRAM_PCH_OFFSET);
return 0;
disable_dev:
pmcdev->ssram_pcidev = NULL;
pci_disable_device(pcidev);
release_dev:
pci_dev_put(pcidev);
return ret;
}
MODULE_IMPORT_NS(INTEL_VSEC);
MODULE_IMPORT_NS(INTEL_PMT_TELEMETRY);
......@@ -53,7 +53,15 @@ const struct pmc_reg_map icl_reg_map = {
int icl_core_init(struct pmc_dev *pmcdev)
{
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
int ret;
pmc->map = &icl_reg_map;
return get_primary_reg_base(pmc);
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
pmc_core_get_low_power_modes(pmcdev);
return ret;
}
This diff is collapsed.
......@@ -10,6 +10,14 @@
#include <linux/pci.h>
#include "core.h"
#include "../pmt/telemetry.h"
/* PMC SSRAM PMT Telemetry GUIDS */
#define SOCP_LPM_REQ_GUID 0x2625030
#define IOEM_LPM_REQ_GUID 0x4357464
#define IOEP_LPM_REQ_GUID 0x5077612
static const u8 MTL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20};
/*
* Die Mapping to Product.
......@@ -465,6 +473,7 @@ const struct pmc_reg_map mtl_socm_reg_map = {
.lpm_sts = mtl_socm_lpm_maps,
.lpm_status_offset = MTL_LPM_STATUS_OFFSET,
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
.lpm_reg_index = MTL_LPM_REG_INDEX,
};
const struct pmc_bit_map mtl_ioep_pfear_map[] = {
......@@ -782,6 +791,13 @@ const struct pmc_reg_map mtl_ioep_reg_map = {
.ltr_show_sts = mtl_ioep_ltr_show_map,
.ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
.ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED,
.lpm_num_maps = ADL_LPM_NUM_MAPS,
.lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
.lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
.lpm_priority_offset = MTL_LPM_PRI_OFFSET,
.lpm_en_offset = MTL_LPM_EN_OFFSET,
.lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
.lpm_reg_index = MTL_LPM_REG_INDEX,
};
const struct pmc_bit_map mtl_ioem_pfear_map[] = {
......@@ -922,6 +938,13 @@ const struct pmc_reg_map mtl_ioem_reg_map = {
.ltr_show_sts = mtl_ioep_ltr_show_map,
.ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
.ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED,
.lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
.lpm_num_maps = ADL_LPM_NUM_MAPS,
.lpm_priority_offset = MTL_LPM_PRI_OFFSET,
.lpm_en_offset = MTL_LPM_EN_OFFSET,
.lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
.lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
.lpm_reg_index = MTL_LPM_REG_INDEX,
};
#define PMC_DEVID_SOCM 0x7e7f
......@@ -929,16 +952,19 @@ const struct pmc_reg_map mtl_ioem_reg_map = {
#define PMC_DEVID_IOEM 0x7ebf
static struct pmc_info mtl_pmc_info_list[] = {
{
.devid = PMC_DEVID_SOCM,
.map = &mtl_socm_reg_map,
.guid = SOCP_LPM_REQ_GUID,
.devid = PMC_DEVID_SOCM,
.map = &mtl_socm_reg_map,
},
{
.devid = PMC_DEVID_IOEP,
.map = &mtl_ioep_reg_map,
.guid = IOEP_LPM_REQ_GUID,
.devid = PMC_DEVID_IOEP,
.map = &mtl_ioep_reg_map,
},
{
.devid = PMC_DEVID_IOEM,
.map = &mtl_ioem_reg_map
.guid = IOEM_LPM_REQ_GUID,
.devid = PMC_DEVID_IOEM,
.map = &mtl_ioem_reg_map
},
{}
};
......@@ -946,34 +972,15 @@ static struct pmc_info mtl_pmc_info_list[] = {
#define MTL_GNA_PCI_DEV 0x7e4c
#define MTL_IPU_PCI_DEV 0x7d19
#define MTL_VPU_PCI_DEV 0x7d1d
static void mtl_set_device_d3(unsigned int device)
{
struct pci_dev *pcidev;
pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
if (pcidev) {
if (!device_trylock(&pcidev->dev)) {
pci_dev_put(pcidev);
return;
}
if (!pcidev->dev.driver) {
dev_info(&pcidev->dev, "Setting to D3hot\n");
pci_set_power_state(pcidev, PCI_D3hot);
}
device_unlock(&pcidev->dev);
pci_dev_put(pcidev);
}
}
/*
* Set power state of select devices that do not have drivers to D3
* so that they do not block Package C entry.
*/
static void mtl_d3_fixup(void)
{
mtl_set_device_d3(MTL_GNA_PCI_DEV);
mtl_set_device_d3(MTL_IPU_PCI_DEV);
mtl_set_device_d3(MTL_VPU_PCI_DEV);
pmc_core_set_device_d3(MTL_GNA_PCI_DEV);
pmc_core_set_device_d3(MTL_IPU_PCI_DEV);
pmc_core_set_device_d3(MTL_VPU_PCI_DEV);
}
static int mtl_resume(struct pmc_dev *pmcdev)
......@@ -987,23 +994,36 @@ static int mtl_resume(struct pmc_dev *pmcdev)
int mtl_core_init(struct pmc_dev *pmcdev)
{
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC];
int ret = 0;
int ret;
int func = 2;
bool ssram_init = true;
mtl_d3_fixup();
pmcdev->suspend = cnl_suspend;
pmcdev->resume = mtl_resume;
pmcdev->regmap_list = mtl_pmc_info_list;
pmc_core_ssram_init(pmcdev);
/* If regbase not assigned, set map and discover using legacy method */
if (!pmc->regbase) {
/*
* If ssram init fails use legacy method to at least get the
* primary PMC
*/
ret = pmc_core_ssram_init(pmcdev, func);
if (ret) {
ssram_init = false;
dev_warn(&pmcdev->pdev->dev,
"ssram init failed, %d, using legacy init\n", ret);
pmc->map = &mtl_socm_reg_map;
ret = get_primary_reg_base(pmc);
if (ret)
return ret;
}
pmc_core_get_low_power_modes(pmcdev);
pmc_core_punit_pmt_init(pmcdev, MTL_PMT_DMU_GUID);
if (ssram_init)
return pmc_core_ssram_get_lpm_reqs(pmcdev);
return 0;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -32,8 +32,7 @@ static ssize_t force_power_store(struct device *dev,
mode = hex_to_bin(buf[0]);
dev_dbg(dev, "force_power: storing %#x\n", mode);
if (mode == 0 || mode == 1) {
status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1,
&input, NULL);
status = wmidev_evaluate_method(to_wmi_device(dev), 0, 1, &input, NULL);
if (ACPI_FAILURE(status)) {
dev_dbg(dev, "force_power: failed to evaluate ACPI method\n");
return -ENODEV;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment