Skip to content

Commit

Permalink
Merge branch 'acpi-pm'
Browse files Browse the repository at this point in the history
* acpi-pm:
  ACPI / PM: Fix acpi_pm_notifier_lock vs flush_workqueue() deadlock
  ACPI / LPSS: Consolidate runtime PM and system sleep handling
  ACPI / PM: Combine device suspend routines
  ACPI / LPIT: Add Low Power Idle Table (LPIT) support
  ACPI / PM: Split code validating need for runtime resume in ->prepare()
  ACPI / PM: Restore acpi_subsys_complete()
  ACPI / PM: Combine two identical device resume routines
  ACPI / PM: Remove stale function header
  • Loading branch information
Rafael J. Wysocki committed Nov 13, 2017
2 parents 28da439 + ff16567 commit 794c335
Show file tree
Hide file tree
Showing 11 changed files with 343 additions and 162 deletions.
25 changes: 25 additions & 0 deletions Documentation/acpi/lpit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
To enumerate platform Low Power Idle states, Intel platforms are using
“Low Power Idle Table” (LPIT). More details about this table can be
downloaded from:
http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf

Residencies for each low power state can be read via FFH
(Function fixed hardware) or a memory mapped interface.

On platforms supporting S0ix sleep states, there can be two types of
residencies:
- CPU PKG C10 (Read via FFH interface)
- Platform Controller Hub (PCH) SLP_S0 (Read via memory mapped interface)

The following attributes are added dynamically to the cpuidle
sysfs attribute group:
/sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us
/sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us

The "low_power_idle_cpu_residency_us" attribute shows time spent
by the CPU package in PKG C10

The "low_power_idle_system_residency_us" attribute shows SLP_S0
residency, or system time spent with the SLP_S0# signal asserted.
This is the lowest possible system power state, achieved only when CPU is in
PKG C10 and all functional blocks in PCH are in a low power state.
5 changes: 5 additions & 0 deletions drivers/acpi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ endif
config ACPI_SPCR_TABLE
bool

config ACPI_LPIT
bool
depends on X86_64
default y

config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
Expand Down
1 change: 1 addition & 0 deletions drivers/acpi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ acpi-$(CONFIG_DEBUG_FS) += debugfs.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o

Expand Down
162 changes: 162 additions & 0 deletions drivers/acpi/acpi_lpit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@

/*
* acpi_lpit.c - LPIT table processing functions
*
* Copyright (C) 2017 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#include <linux/cpu.h>
#include <linux/acpi.h>
#include <asm/msr.h>
#include <asm/tsc.h>

struct lpit_residency_info {
struct acpi_generic_address gaddr;
u64 frequency;
void __iomem *iomem_addr;
};

/* Storage for an memory mapped and FFH based entries */
static struct lpit_residency_info residency_info_mem;
static struct lpit_residency_info residency_info_ffh;

static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
{
int err;

if (io_mem) {
u64 count = 0;
int error;

error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count,
residency_info_mem.gaddr.bit_width);
if (error)
return error;

*counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency);
return 0;
}

err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter);
if (!err) {
u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset +
residency_info_ffh.gaddr. bit_width - 1,
residency_info_ffh.gaddr.bit_offset);

*counter &= mask;
*counter >>= residency_info_ffh.gaddr.bit_offset;
*counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency);
return 0;
}

return -ENODATA;
}

static ssize_t low_power_idle_system_residency_us_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 counter;
int ret;

ret = lpit_read_residency_counter_us(&counter, true);
if (ret)
return ret;

return sprintf(buf, "%llu\n", counter);
}
static DEVICE_ATTR_RO(low_power_idle_system_residency_us);

static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 counter;
int ret;

ret = lpit_read_residency_counter_us(&counter, false);
if (ret)
return ret;

return sprintf(buf, "%llu\n", counter);
}
static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us);

int lpit_read_residency_count_address(u64 *address)
{
if (!residency_info_mem.gaddr.address)
return -EINVAL;

*address = residency_info_mem.gaddr.address;

return 0;
}

static void lpit_update_residency(struct lpit_residency_info *info,
struct acpi_lpit_native *lpit_native)
{
info->frequency = lpit_native->counter_frequency ?
lpit_native->counter_frequency : tsc_khz * 1000;
if (!info->frequency)
info->frequency = 1;

info->gaddr = lpit_native->residency_counter;
if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
info->iomem_addr = ioremap_nocache(info->gaddr.address,
info->gaddr.bit_width / 8);
if (!info->iomem_addr)
return;

/* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_system_residency_us.attr,
"cpuidle");
} else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
/* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_cpu_residency_us.attr,
"cpuidle");
}
}

static void lpit_process(u64 begin, u64 end)
{
while (begin + sizeof(struct acpi_lpit_native) < end) {
struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin;

if (!lpit_native->header.type && !lpit_native->header.flags) {
if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
!residency_info_mem.gaddr.address) {
lpit_update_residency(&residency_info_mem, lpit_native);
} else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
!residency_info_ffh.gaddr.address) {
lpit_update_residency(&residency_info_ffh, lpit_native);
}
}
begin += lpit_native->header.length;
}
}

void acpi_init_lpit(void)
{
acpi_status status;
u64 lpit_begin;
struct acpi_table_lpit *lpit;

status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit);

if (ACPI_FAILURE(status))
return;

lpit_begin = (u64)lpit + sizeof(*lpit);
lpit_process(lpit_begin, lpit_begin + lpit->header.length);
}
84 changes: 38 additions & 46 deletions drivers/acpi/acpi_lpss.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ static int acpi_lpss_activate(struct device *dev)
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;

ret = acpi_dev_runtime_resume(dev);
ret = acpi_dev_resume(dev);
if (ret)
return ret;

Expand All @@ -713,43 +713,9 @@ static int acpi_lpss_activate(struct device *dev)

static void acpi_lpss_dismiss(struct device *dev)
{
acpi_dev_runtime_suspend(dev);
acpi_dev_suspend(dev, false);
}

#ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;

ret = pm_generic_suspend_late(dev);
if (ret)
return ret;

if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_save_ctx(dev, pdata);

return acpi_dev_suspend_late(dev);
}

static int acpi_lpss_resume_early(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;

ret = acpi_dev_resume_early(dev);
if (ret)
return ret;

acpi_lpss_d3_to_d0_delay(pdata);

if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_restore_ctx(dev, pdata);

return pm_generic_resume_early(dev);
}
#endif /* CONFIG_PM_SLEEP */

/* IOSF SB for LPSS island */
#define LPSS_IOSF_UNIT_LPIOEP 0xA0
#define LPSS_IOSF_UNIT_LPIO1 0xAB
Expand Down Expand Up @@ -835,19 +801,15 @@ static void lpss_iosf_exit_d3_state(void)
mutex_unlock(&lpss_iosf_mutex);
}

static int acpi_lpss_runtime_suspend(struct device *dev)
static int acpi_lpss_suspend(struct device *dev, bool wakeup)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;

ret = pm_generic_runtime_suspend(dev);
if (ret)
return ret;

if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_save_ctx(dev, pdata);

ret = acpi_dev_runtime_suspend(dev);
ret = acpi_dev_suspend(dev, wakeup);

/*
* This call must be last in the sequence, otherwise PMC will return
Expand All @@ -860,7 +822,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
return ret;
}

static int acpi_lpss_runtime_resume(struct device *dev)
static int acpi_lpss_resume(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
Expand All @@ -872,7 +834,7 @@ static int acpi_lpss_runtime_resume(struct device *dev)
if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
lpss_iosf_exit_d3_state();

ret = acpi_dev_runtime_resume(dev);
ret = acpi_dev_resume(dev);
if (ret)
return ret;

Expand All @@ -881,7 +843,37 @@ static int acpi_lpss_runtime_resume(struct device *dev)
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_restore_ctx(dev, pdata);

return pm_generic_runtime_resume(dev);
return 0;
}

#ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late(struct device *dev)
{
int ret = pm_generic_suspend_late(dev);

return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
}

static int acpi_lpss_resume_early(struct device *dev)
{
int ret = acpi_lpss_resume(dev);

return ret ? ret : pm_generic_resume_early(dev);
}
#endif /* CONFIG_PM_SLEEP */

static int acpi_lpss_runtime_suspend(struct device *dev)
{
int ret = pm_generic_runtime_suspend(dev);

return ret ? ret : acpi_lpss_suspend(dev, true);
}

static int acpi_lpss_runtime_resume(struct device *dev)
{
int ret = acpi_lpss_resume(dev);

return ret ? ret : pm_generic_runtime_resume(dev);
}
#endif /* CONFIG_PM */

Expand All @@ -894,7 +886,7 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
.prepare = acpi_subsys_prepare,
.complete = pm_complete_with_resume_check,
.complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend,
.suspend_late = acpi_lpss_suspend_late,
.resume_early = acpi_lpss_resume_early,
Expand Down
Loading

0 comments on commit 794c335

Please sign in to comment.