Skip to content

Commit

Permalink
platform/x86: intel_pmc_core: Get LPM requirements for Tiger Lake
Browse files Browse the repository at this point in the history
Platforms that support low power modes (LPM) such as Tiger Lake maintain
requirements for each sub-state that a readable in the PMC. However, unlike
LPM status registers, requirement registers are not memory mapped but are
available from an ACPI _DSM. Collect the requirements for Tiger Lake using
the _DSM method and store in a buffer.

Signed-off-by: Gayatri Kammela <gayatri.kammela@intel.com>
Co-developed-by: David E. Box <david.e.box@linux.intel.com>
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
  • Loading branch information
gkammela authored and intel-lab-lkp committed Apr 17, 2021
1 parent 703c5e2 commit 703038f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
56 changes: 56 additions & 0 deletions drivers/platform/x86/intel_pmc_core.c
Expand Up @@ -23,14 +23,19 @@
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/uaccess.h>
#include <linux/uuid.h>

#include <acpi/acpi_bus.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/msr.h>
#include <asm/tsc.h>

#include "intel_pmc_core.h"

#define ACPI_S0IX_DSM_UUID "57a6512e-3979-4e9d-9708-ff13b2508972"
#define ACPI_GET_LOW_MODE_REGISTERS 1

/* PKGC MSRs are common across Intel Core SoCs */
static const struct pmc_bit_map msr_map[] = {
{"Package C2", MSR_PKG_C2_RESIDENCY},
Expand Down Expand Up @@ -590,6 +595,53 @@ static const struct pmc_reg_map tgl_reg_map = {
.etr3_offset = ETR3_OFFSET,
};

static void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev)
{
struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
const int num_maps = pmcdev->map->lpm_num_maps;
size_t lpm_size = LPM_MAX_NUM_MODES * num_maps * 4;
union acpi_object *out_obj;
struct acpi_device *adev;
guid_t s0ix_dsm_guid;
u32 *lpm_req_regs, *addr;

adev = ACPI_COMPANION(&pdev->dev);
if (!adev)
return;

guid_parse(ACPI_S0IX_DSM_UUID, &s0ix_dsm_guid);

out_obj = acpi_evaluate_dsm(adev->handle, &s0ix_dsm_guid, 0,
ACPI_GET_LOW_MODE_REGISTERS, NULL);
if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
int size = out_obj->buffer.length;

if (size != lpm_size) {
acpi_handle_debug(adev->handle,
"_DSM returned unexpected buffer size,"
" have %d, expect %ld\n", size, lpm_size);
goto free_acpi_obj;
}
} else {
acpi_handle_debug(adev->handle,
"_DSM function 0 evaluation failed\n");
goto free_acpi_obj;
}

addr = (u32 *)out_obj->buffer.pointer;

lpm_req_regs = devm_kzalloc(&pdev->dev, lpm_size * sizeof(u32),
GFP_KERNEL);
if (!lpm_req_regs)
goto free_acpi_obj;

memcpy(lpm_req_regs, addr, lpm_size);
pmcdev->lpm_req_regs = lpm_req_regs;

free_acpi_obj:
ACPI_FREE(out_obj);
}

static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
{
return readl(pmcdev->regbase + reg_offset);
Expand Down Expand Up @@ -1424,10 +1476,14 @@ static int pmc_core_probe(struct platform_device *pdev)
return -ENOMEM;

mutex_init(&pmcdev->lock);

pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(pmcdev);
pmc_core_get_low_power_modes(pmcdev);
pmc_core_do_dmi_quirks(pmcdev);

if (pmcdev->map == &tgl_reg_map)
pmc_core_get_tgl_lpm_reqs(pdev);

/*
* On TGL, due to a hardware limitation, the GBE LTR blocks PC10 when
* a cable is attached. Tell the PMC to ignore it.
Expand Down
2 changes: 2 additions & 0 deletions drivers/platform/x86/intel_pmc_core.h
Expand Up @@ -294,6 +294,7 @@ struct pmc_reg_map {
* @s0ix_counter: S0ix residency (step adjusted)
* @num_lpm_modes: Count of enabled modes
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
* @lpm_req_regs: List of substate requirements
*
* pmc_dev contains info about power management controller device.
*/
Expand All @@ -310,6 +311,7 @@ struct pmc_dev {
u64 s0ix_counter;
int num_lpm_modes;
int lpm_en_modes[LPM_MAX_NUM_MODES];
u32 *lpm_req_regs;
};

#define pmc_for_each_mode(i, mode, pmcdev) \
Expand Down

0 comments on commit 703038f

Please sign in to comment.