Re: [PATCH] ACPI / LPIT: Add Low Power Idle Table (LPIT) support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Oct 5, 2017 at 1:43 AM, Srinivas Pandruvada
<srinivas.pandruvada@xxxxxxxxxxxxxxx> wrote:
> Added functionality to read LPIT table, which provides:
>
> - Sysfs interface to read residency counters via
> /sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us
> /sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us
>
> Here the count "low_power_idle_cpu_residency_us" shows the time spent
> by CPU package in low power state. This is read via MSR interface, which
> points to MSR for PKG C10.
>
> Here the count "low_power_idle_system_residency_us" show the count the
> system was in low power state. This is read via MMIO interface. This
> is mapped to SLP_S0 residency on modern Intel systems. This residency
> is achieved only when CPU is in PKG C10 and all functional blocks are
> in low power state.
>
> It is possible that none of the above counters present or anyone of the
> counter present or all counters present.
>
> For example: On my Kabylake system both of the above counters present.
> After suspend to idle these counts updated and prints:
> 6916179
> 6998564
>
> This counter can be read by tools like turbostat to display. Or it can
> be used to debug, if modern systems are reaching desired low power state.
>
> - Provides an interface to read residency counter memory address
> This address can be used to get the base address of PMC memory mapped IO.
> This is utilized by intel_pmc_core driver to print more debug information.
>
> Link: http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf
> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx>
> ---
>  Documentation/acpi/lpit.txt |  26 ++++++++
>  drivers/acpi/Kconfig        |   5 ++
>  drivers/acpi/Makefile       |   1 +
>  drivers/acpi/acpi_lpit.c    | 157 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/acpi/scan.c         |   3 +
>  include/acpi/acpi_lpit.h    |  34 ++++++++++
>  6 files changed, 226 insertions(+)
>  create mode 100644 Documentation/acpi/lpit.txt
>  create mode 100644 drivers/acpi/acpi_lpit.c
>  create mode 100644 include/acpi/acpi_lpit.h
>
> diff --git a/Documentation/acpi/lpit.txt b/Documentation/acpi/lpit.txt
> new file mode 100644
> index 0000000..6be68c0
> --- /dev/null
> +++ b/Documentation/acpi/lpit.txt
> @@ -0,0 +1,26 @@
> +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.
> +
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 1ce52f8..4bfef0f 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -80,6 +80,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
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 90265ab..6a19bd7 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -56,6 +56,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
>
> diff --git a/drivers/acpi/acpi_lpit.c b/drivers/acpi/acpi_lpit.c
> new file mode 100644
> index 0000000..38d760d
> --- /dev/null
> +++ b/drivers/acpi/acpi_lpit.c
> @@ -0,0 +1,157 @@
> +
> +/*
> + * 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) {
> +               u32 count;
> +
> +               count = readl(residency_info_mem.iomem_addr);
> +               *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;
> +}

I don't see users of this.  Are there any?

> +
> +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);
> +}
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 602f8ff..3f05d43 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -15,6 +15,8 @@
>  #include <linux/dma-mapping.h>
>  #include <linux/platform_data/x86/apple.h>
>
> +#include <acpi/acpi_lpit.h>
> +
>  #include <asm/pgtable.h>
>
>  #include "internal.h"
> @@ -2122,6 +2124,7 @@ int __init acpi_scan_init(void)
>         acpi_int340x_thermal_init();
>         acpi_amba_init();
>         acpi_watchdog_init();
> +       acpi_init_lpit();
>
>         acpi_scan_add_handler(&generic_device_handler);
>
> diff --git a/include/acpi/acpi_lpit.h b/include/acpi/acpi_lpit.h
> new file mode 100644
> index 0000000..5d5285a
> --- /dev/null
> +++ b/include/acpi/acpi_lpit.h
> @@ -0,0 +1,34 @@
> +/*
> + * acpi_lpit.h - LPIT table processing functions interface
> + *
> + * 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.
> + */
> +
> +#ifndef ACPI_LPIT_H
> +#define ACPI_LPIT_H
> +
> +#ifdef CONFIG_ACPI_LPIT
> +
> +void acpi_init_lpit(void);
> +int lpit_read_residency_count_address(u64 *address);
> +
> +#else
> +
> +static inline void acpi_init_lpit(void) { }
> +
> +static inline int lpit_read_residency_count_address(u64 *address)
> +{
> +       return -EINVAL;
> +}
> +
> +#endif
> +#endif

The above can go into internal.h or sleep.h in drivers/acpi/ IMO.
There's not need to export it.

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux