Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 794c3355 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-pm'

* 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
parents 28da4395 ff165679
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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

+162 −0
Original line number Diff line number Diff line

/*
 * 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);
}
+38 −46
Original line number Diff line number Diff line
@@ -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;

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

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

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