* Re: [PATCH v2 2/5] clk: ingenic: Handle setting the Low-Power Mode bit
From: Stephen Boyd @ 2019-06-25 22:48 UTC (permalink / raw)
To: James Hogan, Michael Turquette, Paul Burton, Paul Cercueil,
Ralf Baechle
Cc: od, linux-mips, linux-pm, linux-clk, linux-kernel, Paul Cercueil
In-Reply-To: <20190611180757.32299-2-paul@crapouillou.net>
Quoting Paul Cercueil (2019-06-11 11:07:54)
> The Low-Power Mode, when enabled, will make the "wait" MIPS instruction
> suspend the system.
>
> This is not really clock-related, but this bit happens to be in the
> register set of the CGU.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
Applied to clk-next
^ permalink raw reply
* Re: [PATCH v2 1/5] clk: ingenic: Add missing header in cgu.h
From: Paul Cercueil @ 2019-06-25 22:46 UTC (permalink / raw)
To: Stephen Boyd
Cc: James Hogan, Michael Turquette, Paul Burton, Ralf Baechle, od,
linux-mips, linux-pm, linux-clk, linux-kernel
In-Reply-To: <20190625224221.2643520645@mail.kernel.org>
Le mer. 26 juin 2019 à 0:42, Stephen Boyd <sboyd@kernel.org> a écrit :
> Quoting Stephen Boyd (2019-06-25 15:17:35)
>> Quoting Paul Cercueil (2019-06-11 11:07:53)
>> > The cgu.h has structures that contain 'clk_onecell_data' and
>> 'clk_hw'
>> > structures (no pointers), so the <linux/clk-provider.h> header
>> should be
>> > included.
>> >
>> > Signed-off-by: Paul Cercueil <paul@crapouillou.net>
>> > ---
>> >
>> > Notes:
>> > v2: Rebase on v5.2-rc4
>>
>> You seemed to miss my review comments on patch set #1.
>>
>>
>> https://lkml.kernel.org/r/155726035790.14659.7321778387595703949@swboyd.mtv.corp.google.com
Oops. Sorry about that.
> I've just decided to fix it instead of wait. Let me know if that's a
> problem.
Sure, that's no problem. Thanks!
-Paul
^ permalink raw reply
* Re: [PATCH v2 1/5] clk: ingenic: Add missing header in cgu.h
From: Stephen Boyd @ 2019-06-25 22:42 UTC (permalink / raw)
To: James Hogan, Michael Turquette, Paul Burton, Paul Cercueil,
Ralf Baechle
Cc: od, linux-mips, linux-pm, linux-clk, linux-kernel, Paul Cercueil
In-Reply-To: <20190625221736.853892086D@mail.kernel.org>
Quoting Stephen Boyd (2019-06-25 15:17:35)
> Quoting Paul Cercueil (2019-06-11 11:07:53)
> > The cgu.h has structures that contain 'clk_onecell_data' and 'clk_hw'
> > structures (no pointers), so the <linux/clk-provider.h> header should be
> > included.
> >
> > Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> > ---
> >
> > Notes:
> > v2: Rebase on v5.2-rc4
>
> You seemed to miss my review comments on patch set #1.
>
> https://lkml.kernel.org/r/155726035790.14659.7321778387595703949@swboyd.mtv.corp.google.com
>
I've just decided to fix it instead of wait. Let me know if that's a
problem.
^ permalink raw reply
* Re: [PATCH] test_power: Add CURRENT and CHARGE_COUNTER properties
From: Sebastian Reichel @ 2019-06-25 22:35 UTC (permalink / raw)
To: Lecopzer Chen
Cc: linux-pm, linux-kernel, srv_heupstream, yj.chiang, linux-mediatek
In-Reply-To: <1557828298-16591-1-git-send-email-lecopzer.chen@mediatek.com>
[-- Attachment #1: Type: text/plain, Size: 5034 bytes --]
Hi,
On Tue, May 14, 2019 at 06:04:58PM +0800, Lecopzer Chen wrote:
> From: "Lecopzer Chen" <lecopzer.chen@mediatek.com>
>
> Emulate battery current (variable) and battery CHARGE_COUNTER
> (same as battery_capacity) properties.
>
> Signed-off-by: Lecopzer Chen <lecopzer.chen@mediatek.com>
> Cc: YJ Chiang <yj.chiang@mediatek.com>
Thanks for the patch and thanks for taking care of testing infrastructure.
I don't think it's a good idea to use CHARGE_CAPACITY/CHARGE_NOW also for
CHARGE_COUNTER. CHARGE_COUNTER may be negative, but capacity should not be
negative. Also the patch should be splitted (one for the current feature and
one for the charge counter feature). Last but not least I don't see any
conversion from mA to µA. The userspace sysfs ABI should always
return µA.
-- Sebastian
> ---
> drivers/power/supply/test_power.c | 33 +++++++++++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c
> index 57246cdbd042..9f85060c84de 100644
> --- a/drivers/power/supply/test_power.c
> +++ b/drivers/power/supply/test_power.c
> @@ -36,6 +36,7 @@ static int battery_present = 1; /* true */
> static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION;
> static int battery_capacity = 50;
> static int battery_voltage = 3300;
> +static int battery_current = 1600;
>
> static bool module_initialized;
>
> @@ -101,6 +102,7 @@ static int test_power_get_battery_property(struct power_supply *psy,
> break;
> case POWER_SUPPLY_PROP_CAPACITY:
> case POWER_SUPPLY_PROP_CHARGE_NOW:
> + case POWER_SUPPLY_PROP_CHARGE_COUNTER:
> val->intval = battery_capacity;
> break;
> case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
> @@ -117,6 +119,10 @@ static int test_power_get_battery_property(struct power_supply *psy,
> case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> val->intval = battery_voltage;
> break;
> + case POWER_SUPPLY_PROP_CURRENT_AVG:
> + case POWER_SUPPLY_PROP_CURRENT_NOW:
> + val->intval = battery_current;
> + break;
> default:
> pr_info("%s: some properties deliberately report errors.\n",
> __func__);
> @@ -138,6 +144,7 @@ static enum power_supply_property test_power_battery_props[] = {
> POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
> POWER_SUPPLY_PROP_CHARGE_FULL,
> POWER_SUPPLY_PROP_CHARGE_NOW,
> + POWER_SUPPLY_PROP_CHARGE_COUNTER,
> POWER_SUPPLY_PROP_CAPACITY,
> POWER_SUPPLY_PROP_CAPACITY_LEVEL,
> POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
> @@ -147,6 +154,8 @@ static enum power_supply_property test_power_battery_props[] = {
> POWER_SUPPLY_PROP_SERIAL_NUMBER,
> POWER_SUPPLY_PROP_TEMP,
> POWER_SUPPLY_PROP_VOLTAGE_NOW,
> + POWER_SUPPLY_PROP_CURRENT_AVG,
> + POWER_SUPPLY_PROP_CURRENT_NOW,
> };
>
> static char *test_power_ac_supplied_to[] = {
> @@ -450,6 +459,21 @@ static int param_set_battery_voltage(const char *key,
>
> #define param_get_battery_voltage param_get_int
>
> +static int param_set_battery_current(const char *key,
> + const struct kernel_param *kp)
> +{
> + int tmp;
> +
> + if (1 != sscanf(key, "%d", &tmp))
> + return -EINVAL;
> +
> + battery_current = tmp;
> + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]);
> + return 0;
> +}
> +
> +#define param_get_battery_current param_get_int
> +
> static const struct kernel_param_ops param_ops_ac_online = {
> .set = param_set_ac_online,
> .get = param_get_ac_online,
> @@ -490,6 +514,11 @@ static const struct kernel_param_ops param_ops_battery_voltage = {
> .get = param_get_battery_voltage,
> };
>
> +static const struct kernel_param_ops param_ops_battery_current = {
> + .set = param_set_battery_current,
> + .get = param_get_battery_current,
> +};
> +
> #define param_check_ac_online(name, p) __param_check(name, p, void);
> #define param_check_usb_online(name, p) __param_check(name, p, void);
> #define param_check_battery_status(name, p) __param_check(name, p, void);
> @@ -498,6 +527,7 @@ static const struct kernel_param_ops param_ops_battery_voltage = {
> #define param_check_battery_health(name, p) __param_check(name, p, void);
> #define param_check_battery_capacity(name, p) __param_check(name, p, void);
> #define param_check_battery_voltage(name, p) __param_check(name, p, void);
> +#define param_check_battery_current(name, p) __param_check(name, p, void);
>
>
> module_param(ac_online, ac_online, 0644);
> @@ -528,6 +558,9 @@ MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)");
> module_param(battery_voltage, battery_voltage, 0644);
> MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)");
>
> +module_param(battery_current, battery_current, 0644);
> +MODULE_PARM_DESC(battery_current, "battery current (milliampere)");
> +
> MODULE_DESCRIPTION("Power supply driver for testing");
> MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
> MODULE_LICENSE("GPL");
> --
> 2.18.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH] PCI: PM: Avoid skipping bus-level PM on platforms without ACPI
From: Rafael J. Wysocki @ 2019-06-25 22:20 UTC (permalink / raw)
To: Linux PCI, Linux PM
Cc: Jon Hunter, Bjorn Helgaas, LKML, Mika Westerberg, Kai-Heng Feng
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
There are platforms that do not call pm_set_suspend_via_firmware(),
so pm_suspend_via_firmware() returns 'false' on them, but the power
states of PCI devices (PCIe ports in particular) are changed as a
result of powering down core platform components during system-wide
suspend. Thus the pm_suspend_via_firmware() checks in
pci_pm_suspend_noirq() and pci_pm_resume_noirq() introduced by
commit 3e26c5feed2a ("PCI: PM: Skip devices in D0 for suspend-to-
idle") are not sufficient to determine that devices left in D0
during suspend will remain in D0 during resume and so the bus-level
power management can be skipped for them.
For this reason, introduce a new global suspend flag,
PM_SUSPEND_FLAG_NO_PLATFORM, set it for suspend-to-idle only
and replace the pm_suspend_via_firmware() checks mentioned above
with checks against this flag.
Fixes: 3e26c5feed2a ("PCI: PM: Skip devices in D0 for suspend-to-idle")
Reported-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/pci/pci-driver.c | 8 ++++----
include/linux/suspend.h | 26 ++++++++++++++++++++++++--
kernel/power/suspend.c | 3 +++
3 files changed, 31 insertions(+), 6 deletions(-)
Index: linux-pm/include/linux/suspend.h
===================================================================
--- linux-pm.orig/include/linux/suspend.h
+++ linux-pm/include/linux/suspend.h
@@ -209,8 +209,9 @@ extern int suspend_valid_only_mem(suspen
extern unsigned int pm_suspend_global_flags;
-#define PM_SUSPEND_FLAG_FW_SUSPEND (1 << 0)
-#define PM_SUSPEND_FLAG_FW_RESUME (1 << 1)
+#define PM_SUSPEND_FLAG_FW_SUSPEND BIT(0)
+#define PM_SUSPEND_FLAG_FW_RESUME BIT(1)
+#define PM_SUSPEND_FLAG_NO_PLATFORM BIT(2)
static inline void pm_suspend_clear_flags(void)
{
@@ -227,6 +228,11 @@ static inline void pm_set_resume_via_fir
pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_RESUME;
}
+static inline void pm_set_suspend_no_platform(void)
+{
+ pm_suspend_global_flags |= PM_SUSPEND_FLAG_NO_PLATFORM;
+}
+
/**
* pm_suspend_via_firmware - Check if platform firmware will suspend the system.
*
@@ -268,6 +274,22 @@ static inline bool pm_resume_via_firmwar
return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_RESUME);
}
+/**
+ * pm_suspend_no_platform - Check if platform may change device power states.
+ *
+ * To be called during system-wide power management transitions to sleep states
+ * or during the subsequent system-wide transitions back to the working state.
+ *
+ * Return 'true' if the power states of devices remain under full control of the
+ * kernel throughout the system-wide suspend and resume cycle in progress (that
+ * is, if a device is put into a certain power state during suspend, it can be
+ * expected to remain in that state during resume).
+ */
+static inline bool pm_suspend_no_platform(void)
+{
+ return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_NO_PLATFORM);
+}
+
/* Suspend-to-idle state machnine. */
enum s2idle_states {
S2IDLE_STATE_NONE, /* Not suspended/suspending. */
Index: linux-pm/kernel/power/suspend.c
===================================================================
--- linux-pm.orig/kernel/power/suspend.c
+++ linux-pm/kernel/power/suspend.c
@@ -493,6 +493,9 @@ int suspend_devices_and_enter(suspend_st
pm_suspend_target_state = state;
+ if (state == PM_SUSPEND_TO_IDLE)
+ pm_set_suspend_no_platform();
+
error = platform_suspend_begin(state);
if (error)
goto Close;
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -877,7 +877,7 @@ static int pci_pm_suspend_noirq(struct d
pci_dev->bus->self->skip_bus_pm = true;
}
- if (pci_dev->skip_bus_pm && !pm_suspend_via_firmware()) {
+ if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
dev_dbg(dev, "PCI PM: Skipped\n");
goto Fixup;
}
@@ -932,10 +932,10 @@ static int pci_pm_resume_noirq(struct de
/*
* In the suspend-to-idle case, devices left in D0 during suspend will
* stay in D0, so it is not necessary to restore or update their
- * configuration here and attempting to put them into D0 again may
- * confuse some firmware, so avoid doing that.
+ * configuration here and attempting to put them into D0 again is
+ * pointless, so avoid doing that.
*/
- if (!pci_dev->skip_bus_pm || pm_suspend_via_firmware())
+ if (!(pci_dev->skip_bus_pm && pm_suspend_no_platform()))
pci_pm_default_resume_early(pci_dev);
pci_fixup_device(pci_fixup_resume_early, pci_dev);
^ permalink raw reply
* Re: [PATCH v2 1/5] clk: ingenic: Add missing header in cgu.h
From: Stephen Boyd @ 2019-06-25 22:17 UTC (permalink / raw)
To: James Hogan, Michael Turquette, Paul Burton, Paul Cercueil,
Ralf Baechle
Cc: od, linux-mips, linux-pm, linux-clk, linux-kernel, Paul Cercueil
In-Reply-To: <20190611180757.32299-1-paul@crapouillou.net>
Quoting Paul Cercueil (2019-06-11 11:07:53)
> The cgu.h has structures that contain 'clk_onecell_data' and 'clk_hw'
> structures (no pointers), so the <linux/clk-provider.h> header should be
> included.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
>
> Notes:
> v2: Rebase on v5.2-rc4
You seemed to miss my review comments on patch set #1.
https://lkml.kernel.org/r/155726035790.14659.7321778387595703949@swboyd.mtv.corp.google.com
^ permalink raw reply
* Re: [PATCH 2/2] dt-bindings: power: supply: Add documentation for the VINDPM properties
From: Sebastian Reichel @ 2019-06-25 22:13 UTC (permalink / raw)
To: Rob Herring
Cc: Angus Ainslie (Purism), angus.ainslie, Mark Rutland, linux-pm,
devicetree, linux-kernel
In-Reply-To: <20190613230906.GA10492@bogus>
[-- Attachment #1: Type: text/plain, Size: 2273 bytes --]
Hi,
On Thu, Jun 13, 2019 at 05:09:06PM -0600, Rob Herring wrote:
> On Mon, May 20, 2019 at 11:07:12AM -0700, Angus Ainslie (Purism) wrote:
> > Add documentation on how to control VINDPM from the devicetree.
> >
> > Signed-off-by: Angus Ainslie (Purism) <angus@akkea.ca>
> > ---
> > .../devicetree/bindings/power/supply/bq25890.txt | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/power/supply/bq25890.txt b/Documentation/devicetree/bindings/power/supply/bq25890.txt
> > index dc0568933359..fe8b709dd666 100644
> > --- a/Documentation/devicetree/bindings/power/supply/bq25890.txt
> > +++ b/Documentation/devicetree/bindings/power/supply/bq25890.txt
> > @@ -26,9 +26,15 @@ Optional properties:
> > - ti,use-ilim-pin: boolean, if present the ILIM resistor will be used and the
> > input current will be the lower between the resistor setting and the IINLIM
> > register setting;
> > +- ti,use-vinmin-threshold: boolean, if present the FORCE_VINDPM bit will be set
> > + and the input voltage limit will be configured based on "ti,vinmin-threshold"
>
> Isn't presence of ti,vinmin-threshold enough to determine whether to set
> FORCE_VINDPM or not? Just get rid of the default being 4.4V.
>
> > - ti,thermal-regulation-threshold: integer, temperature above which the charge
> > current is lowered, to avoid overheating (in degrees Celsius). If omitted,
> > the default setting will be used (120 degrees);
> > +- ti,vinmin-threshold: integer, lower absolute threshold for VINDPM. If the
> > + voltage falls below this threshold the charge current is reduced until the
> > + input voltage rises above the input voltage limit. If omitted, the default
> > + setting will be used (4.4V);
We already have a "input-voltage-min-microvolt" property used by
Maxim chargers, please resuse that for the bq25890 instead of
creating a new property name.
-- Sebastian
> >
> > Example:
> >
> > @@ -46,4 +52,6 @@ bq25890 {
> >
> > ti,use-ilim-pin;
> > ti,thermal-regulation-threshold = <120>;
> > + ti,use-vinmin-threshold;
> > + ti,vinmin-threshold = <3900000>;
> > };
> > --
> > 2.17.1
> >
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [patch 3/5] cpuidle: add haltpoll governor
From: Rafael J. Wysocki @ 2019-06-25 21:57 UTC (permalink / raw)
To: Marcelo Tosatti
Cc: kvm-devel, Paolo Bonzini, Radim Krcmar, Andrea Arcangeli,
Rafael J. Wysocki, Peter Zijlstra, Wanpeng Li,
Konrad Rzeszutek Wilk, Raslan KarimAllah, Boris Ostrovsky,
Ankur Arora, Christian Borntraeger, Linux PM
In-Reply-To: <20190613225023.011025297@redhat.com>
On Fri, Jun 14, 2019 at 12:55 AM Marcelo Tosatti <mtosatti@redhat.com> wrote:
>
> The cpuidle_haltpoll governor, in conjunction with the haltpoll cpuidle
> driver, allows guest vcpus to poll for a specified amount of time before
> halting.
> This provides the following benefits to host side polling:
>
> 1) The POLL flag is set while polling is performed, which allows
> a remote vCPU to avoid sending an IPI (and the associated
> cost of handling the IPI) when performing a wakeup.
>
> 2) The VM-exit cost can be avoided.
>
> The downside of guest side polling is that polling is performed
> even with other runnable tasks in the host.
>
> Results comparing halt_poll_ns and server/client application
> where a small packet is ping-ponged:
>
> host --> 31.33
> halt_poll_ns=300000 / no guest busy spin --> 33.40 (93.8%)
> halt_poll_ns=0 / guest_halt_poll_ns=300000 --> 32.73 (95.7%)
>
> For the SAP HANA benchmarks (where idle_spin is a parameter
> of the previous version of the patch, results should be the
> same):
>
> hpns == halt_poll_ns
>
> idle_spin=0/ idle_spin=800/ idle_spin=0/
> hpns=200000 hpns=0 hpns=800000
> DeleteC06T03 (100 thread) 1.76 1.71 (-3%) 1.78 (+1%)
> InsertC16T02 (100 thread) 2.14 2.07 (-3%) 2.18 (+1.8%)
> DeleteC00T01 (1 thread) 1.34 1.28 (-4.5%) 1.29 (-3.7%)
> UpdateC00T03 (1 thread) 4.72 4.18 (-12%) 4.53 (-5%)
>
> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
>
>
> ---
> Documentation/virtual/guest-halt-polling.txt | 79 ++++++++++++
> drivers/cpuidle/Kconfig | 11 +
> drivers/cpuidle/governors/Makefile | 1
> drivers/cpuidle/governors/haltpoll.c | 175 +++++++++++++++++++++++++++
> 4 files changed, 266 insertions(+)
>
> Index: linux-2.6.git/drivers/cpuidle/Kconfig
> ===================================================================
> --- linux-2.6.git.orig/drivers/cpuidle/Kconfig 2019-06-13 18:05:46.456294042 -0400
> +++ linux-2.6.git/drivers/cpuidle/Kconfig 2019-06-13 18:14:58.981570277 -0400
> @@ -33,6 +33,17 @@
> Some workloads benefit from using it and it generally should be safe
> to use. Say Y here if you are not happy with the alternatives.
>
> +config CPU_IDLE_GOV_HALTPOLL
> + bool "Haltpoll governor (for virtualized systems)"
> + depends on KVM_GUEST
> + help
> + This governor implements haltpoll idle state selection, to be
> + used in conjunction with the haltpoll cpuidle driver, allowing
> + for polling for a certain amount of time before entering idle
> + state.
> +
> + Some virtualized workloads benefit from using it.
> +
> config DT_IDLE_STATES
> bool
>
> Index: linux-2.6.git/drivers/cpuidle/governors/Makefile
> ===================================================================
> --- linux-2.6.git.orig/drivers/cpuidle/governors/Makefile 2019-06-13 18:05:46.456294042 -0400
> +++ linux-2.6.git/drivers/cpuidle/governors/Makefile 2019-06-13 18:10:53.861444033 -0400
> @@ -6,3 +6,4 @@
> obj-$(CONFIG_CPU_IDLE_GOV_LADDER) += ladder.o
> obj-$(CONFIG_CPU_IDLE_GOV_MENU) += menu.o
> obj-$(CONFIG_CPU_IDLE_GOV_TEO) += teo.o
> +obj-$(CONFIG_CPU_IDLE_GOV_HALTPOLL) += haltpoll.o
> Index: linux-2.6.git/drivers/cpuidle/governors/haltpoll.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.git/drivers/cpuidle/governors/haltpoll.c 2019-06-13 18:12:46.581615748 -0400
> @@ -0,0 +1,175 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * haltpoll.c - haltpoll idle governor
> + *
> + * Copyright 2019 Red Hat, Inc. and/or its affiliates.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + *
> + * Authors: Marcelo Tosatti <mtosatti@redhat.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/cpuidle.h>
> +#include <linux/time.h>
> +#include <linux/ktime.h>
> +#include <linux/hrtimer.h>
> +#include <linux/tick.h>
> +#include <linux/sched.h>
> +#include <linux/module.h>
> +#include <linux/kvm_para.h>
> +
> +static unsigned int guest_halt_poll_us __read_mostly = 200;
> +module_param(guest_halt_poll_us, uint, 0644);
> +
> +/* division factor to shrink halt_poll_us */
> +static unsigned int guest_halt_poll_shrink __read_mostly = 2;
> +module_param(guest_halt_poll_shrink, uint, 0644);
> +
> +/* multiplication factor to grow per-cpu halt_poll_us */
> +static unsigned int guest_halt_poll_grow __read_mostly = 2;
> +module_param(guest_halt_poll_grow, uint, 0644);
> +
> +/* value in us to start growing per-cpu halt_poll_us */
> +static unsigned int guest_halt_poll_grow_start __read_mostly = 50;
> +module_param(guest_halt_poll_grow_start, uint, 0644);
> +
> +/* allow shrinking guest halt poll */
> +static bool guest_halt_poll_allow_shrink __read_mostly = true;
> +module_param(guest_halt_poll_allow_shrink, bool, 0644);
> +
> +struct haltpoll_device {
> + int last_state_idx;
> + unsigned int halt_poll_us;
> +};
> +
> +static DEFINE_PER_CPU_ALIGNED(struct haltpoll_device, hpoll_devices);
> +
> +/**
> + * haltpoll_select - selects the next idle state to enter
> + * @drv: cpuidle driver containing state data
> + * @dev: the CPU
> + * @stop_tick: indication on whether or not to stop the tick
> + */
> +static int haltpoll_select(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev,
> + bool *stop_tick)
> +{
> + struct haltpoll_device *hdev = this_cpu_ptr(&hpoll_devices);
What about PM QoS constraints?
Especially if the resume latency limit is set to 0?
> +
> + if (!drv->state_count) {
> + *stop_tick = false;
> + return 0;
> + }
> +
> + if (hdev->halt_poll_us == 0)
> + return 1;
> +
> + /* Last state was poll? */
> + if (hdev->last_state_idx == 0) {
> + /* Halt if no event occurred on poll window */
> + if (dev->poll_time_limit == true)
> + return 1;
> +
> + *stop_tick = false;
> + /* Otherwise, poll again */
> + return 0;
> + }
> +
> + *stop_tick = false;
> + /* Last state was halt: poll */
> + return 0;
> +}
> +
> +static void adjust_haltpoll_us(unsigned int block_us,
> + struct haltpoll_device *dev)
> +{
> + unsigned int val;
> +
> + /* Grow cpu_halt_poll_us if
> + * cpu_halt_poll_us < block_ns < guest_halt_poll_us
> + */
> + if (block_us > dev->halt_poll_us && block_us <= guest_halt_poll_us) {
> + val = dev->halt_poll_us * guest_halt_poll_grow;
> +
> + if (val < guest_halt_poll_grow_start)
> + val = guest_halt_poll_grow_start;
> + if (val > guest_halt_poll_us)
> + val = guest_halt_poll_us;
> +
> + dev->halt_poll_us = val;
> + } else if (block_us > guest_halt_poll_us &&
> + guest_halt_poll_allow_shrink) {
> + unsigned int shrink = guest_halt_poll_shrink;
> +
> + val = dev->halt_poll_us;
> + if (shrink == 0)
> + val = 0;
> + else
> + val /= shrink;
> + dev->halt_poll_us = val;
> + }
> +}
> +
> +/**
> + * haltpoll_reflect - update variables and update poll time
> + * @dev: the CPU
> + * @index: the index of actual entered state
> + */
> +static void haltpoll_reflect(struct cpuidle_device *dev, int index)
> +{
> + struct haltpoll_device *hdev = this_cpu_ptr(&hpoll_devices);
> +
> + hdev->last_state_idx = index;
> +
> + if (index != 0)
> + adjust_haltpoll_us(dev->last_residency, hdev);
> +}
> +
> +/**
> + * haltpoll_enable_device - scans a CPU's states and does setup
> + * @drv: cpuidle driver
> + * @dev: the CPU
> + */
> +static int haltpoll_enable_device(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev)
> +{
> + struct haltpoll_device *hdev = &per_cpu(hpoll_devices, dev->cpu);
> +
> + memset(hdev, 0, sizeof(struct haltpoll_device));
> +
> + return 0;
> +}
> +
> +/**
> + * haltpoll_get_poll_time - return amount of poll time
> + * @drv: cpuidle driver
> + * @dev: the CPU
> + */
> +static u64 haltpoll_get_poll_time(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev)
> +{
> + struct haltpoll_device *hdev = &per_cpu(hpoll_devices, dev->cpu);
> +
> + return hdev->halt_poll_us * NSEC_PER_USEC;
> +}
> +
> +static struct cpuidle_governor haltpoll_governor = {
> + .name = "haltpoll",
> + .rating = 21,
> + .enable = haltpoll_enable_device,
> + .select = haltpoll_select,
> + .reflect = haltpoll_reflect,
> + .get_poll_time = haltpoll_get_poll_time,
> +};
> +
> +static int __init init_haltpoll(void)
> +{
> + if (kvm_para_available())
> + return cpuidle_register_governor(&haltpoll_governor);
> +
> + return 0;
> +}
> +
> +postcore_initcall(init_haltpoll);
> Index: linux-2.6.git/Documentation/virtual/guest-halt-polling.txt
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.git/Documentation/virtual/guest-halt-polling.txt 2019-06-13 18:16:22.414262777 -0400
> @@ -0,0 +1,79 @@
> +Guest halt polling
> +==================
> +
> +The cpuidle_haltpoll driver, with the haltpoll governor, allows
> +the guest vcpus to poll for a specified amount of time before
> +halting.
> +This provides the following benefits to host side polling:
> +
> + 1) The POLL flag is set while polling is performed, which allows
> + a remote vCPU to avoid sending an IPI (and the associated
> + cost of handling the IPI) when performing a wakeup.
> +
> + 2) The VM-exit cost can be avoided.
> +
> +The downside of guest side polling is that polling is performed
> +even with other runnable tasks in the host.
> +
> +The basic logic as follows: A global value, guest_halt_poll_us,
> +is configured by the user, indicating the maximum amount of
> +time polling is allowed. This value is fixed.
> +
> +Each vcpu has an adjustable guest_halt_poll_us
> +("per-cpu guest_halt_poll_us"), which is adjusted by the algorithm
> +in response to events (explained below).
> +
> +Module Parameters
> +=================
> +
> +The haltpoll governor has 5 tunable module parameters:
> +
> +1) guest_halt_poll_us:
> +Maximum amount of time, in microseconds, that polling is
> +performed before halting.
> +
> +Default: 200
> +
> +2) guest_halt_poll_shrink:
> +Division factor used to shrink per-cpu guest_halt_poll_us when
> +wakeup event occurs after the global guest_halt_poll_us.
> +
> +Default: 2
> +
> +3) guest_halt_poll_grow:
> +Multiplication factor used to grow per-cpu guest_halt_poll_us
> +when event occurs after per-cpu guest_halt_poll_us
> +but before global guest_halt_poll_us.
> +
> +Default: 2
> +
> +4) guest_halt_poll_grow_start:
> +The per-cpu guest_halt_poll_us eventually reaches zero
> +in case of an idle system. This value sets the initial
> +per-cpu guest_halt_poll_us when growing. This can
> +be increased from 10, to avoid misses during the initial
> +growth stage:
> +
> +10, 20, 40, ... (example assumes guest_halt_poll_grow=2).
> +
> +Default: 50
> +
> +5) guest_halt_poll_allow_shrink:
> +
> +Bool parameter which allows shrinking. Set to N
> +to avoid it (per-cpu guest_halt_poll_us will remain
> +high once achieves global guest_halt_poll_us value).
> +
> +Default: Y
> +
> +The module parameters can be set from the debugfs files in:
> +
> + /sys/module/haltpoll/parameters/
> +
> +Further Notes
> +=============
> +
> +- Care should be taken when setting the guest_halt_poll_us parameter as a
> +large value has the potential to drive the cpu usage to 100% on a machine which
> +would be almost entirely idle otherwise.
> +
>
>
^ permalink raw reply
* Re: [patch 2/5] cpuidle: add get_poll_time callback
From: Rafael J. Wysocki @ 2019-06-25 21:52 UTC (permalink / raw)
To: Marcelo Tosatti
Cc: kvm-devel, Paolo Bonzini, Radim Krcmar, Andrea Arcangeli,
Rafael J. Wysocki, Peter Zijlstra, Wanpeng Li,
Konrad Rzeszutek Wilk, Raslan KarimAllah, Boris Ostrovsky,
Ankur Arora, Christian Borntraeger, Linux PM
In-Reply-To: <20190613225022.969533311@redhat.com>
On Fri, Jun 14, 2019 at 12:55 AM Marcelo Tosatti <mtosatti@redhat.com> wrote:
>
> Add a "get_poll_time" callback to the cpuidle_governor structure,
> and change poll state to poll for that amount of time.
>
> Provide a default method for it, while allowing individual governors
> to override it.
>
> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Fair enough:
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> ---
> drivers/cpuidle/cpuidle.c | 40 ++++++++++++++++++++++++++++++++++++++++
> drivers/cpuidle/poll_state.c | 11 ++---------
> include/linux/cpuidle.h | 8 ++++++++
> 3 files changed, 50 insertions(+), 9 deletions(-)
>
> Index: linux-2.6.git/drivers/cpuidle/cpuidle.c
> ===================================================================
> --- linux-2.6.git.orig/drivers/cpuidle/cpuidle.c 2019-06-13 17:57:33.111185824 -0400
> +++ linux-2.6.git/drivers/cpuidle/cpuidle.c 2019-06-13 18:09:48.158500660 -0400
> @@ -362,6 +362,46 @@
> }
>
> /**
> + * cpuidle_default_poll_time - default routine used to return poll time
> + * governors can override it if necessary
> + *
> + * @drv: the cpuidle driver tied with the cpu
> + * @dev: the cpuidle device
> + *
> + */
> +static u64 cpuidle_default_poll_time(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev)
> +{
> + int i;
> +
> + for (i = 1; i < drv->state_count; i++) {
> + if (drv->states[i].disabled || dev->states_usage[i].disable)
> + continue;
> +
> + return (u64)drv->states[i].target_residency * NSEC_PER_USEC;
> + }
> +
> + return TICK_NSEC;
> +}
> +
> +/**
> + * cpuidle_get_poll_time - tell the polling driver how much time to poll,
> + * in nanoseconds.
> + *
> + * @drv: the cpuidle driver tied with the cpu
> + * @dev: the cpuidle device
> + *
> + */
> +u64 cpuidle_get_poll_time(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev)
> +{
> + if (cpuidle_curr_governor->get_poll_time)
> + return cpuidle_curr_governor->get_poll_time(drv, dev);
> +
> + return cpuidle_default_poll_time(drv, dev);
> +}
> +
> +/**
> * cpuidle_install_idle_handler - installs the cpuidle idle loop handler
> */
> void cpuidle_install_idle_handler(void)
> Index: linux-2.6.git/drivers/cpuidle/poll_state.c
> ===================================================================
> --- linux-2.6.git.orig/drivers/cpuidle/poll_state.c 2019-06-13 17:57:33.111185824 -0400
> +++ linux-2.6.git/drivers/cpuidle/poll_state.c 2019-06-13 18:01:19.846944820 -0400
> @@ -20,16 +20,9 @@
> local_irq_enable();
> if (!current_set_polling_and_test()) {
> unsigned int loop_count = 0;
> - u64 limit = TICK_NSEC;
> - int i;
> + u64 limit;
>
> - for (i = 1; i < drv->state_count; i++) {
> - if (drv->states[i].disabled || dev->states_usage[i].disable)
> - continue;
> -
> - limit = (u64)drv->states[i].target_residency * NSEC_PER_USEC;
> - break;
> - }
> + limit = cpuidle_get_poll_time(drv, dev);
>
> while (!need_resched()) {
> cpu_relax();
> Index: linux-2.6.git/include/linux/cpuidle.h
> ===================================================================
> --- linux-2.6.git.orig/include/linux/cpuidle.h 2019-06-13 17:57:33.111185824 -0400
> +++ linux-2.6.git/include/linux/cpuidle.h 2019-06-13 18:01:19.846944820 -0400
> @@ -132,6 +132,8 @@
> extern int cpuidle_enter(struct cpuidle_driver *drv,
> struct cpuidle_device *dev, int index);
> extern void cpuidle_reflect(struct cpuidle_device *dev, int index);
> +extern u64 cpuidle_get_poll_time(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev);
>
> extern int cpuidle_register_driver(struct cpuidle_driver *drv);
> extern struct cpuidle_driver *cpuidle_get_driver(void);
> @@ -166,6 +168,9 @@
> struct cpuidle_device *dev, int index)
> {return -ENODEV; }
> static inline void cpuidle_reflect(struct cpuidle_device *dev, int index) { }
> +extern u64 cpuidle_get_poll_time(struct cpuidle_driver *drv,
> + struct cpuidle_device *dev)
> +{return 0; }
> static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
> {return -ENODEV; }
> static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }
> @@ -246,6 +251,9 @@
> struct cpuidle_device *dev,
> bool *stop_tick);
> void (*reflect) (struct cpuidle_device *dev, int index);
> +
> + u64 (*get_poll_time) (struct cpuidle_driver *drv,
> + struct cpuidle_device *dev);
> };
>
> #ifdef CONFIG_CPU_IDLE
>
>
^ permalink raw reply
* Re: [patch 1/5] drivers/cpuidle: add cpuidle-haltpoll driver
From: Rafael J. Wysocki @ 2019-06-25 21:50 UTC (permalink / raw)
To: Marcelo Tosatti
Cc: kvm-devel, Paolo Bonzini, Radim Krcmar, Andrea Arcangeli,
Rafael J. Wysocki, Peter Zijlstra, Wanpeng Li,
Konrad Rzeszutek Wilk, Raslan KarimAllah, Boris Ostrovsky,
Ankur Arora, Christian Borntraeger, Linux PM
In-Reply-To: <20190613225022.932697232@redhat.com>
On Fri, Jun 14, 2019 at 12:55 AM Marcelo Tosatti <mtosatti@redhat.com> wrote:
>
> Add a cpuidle driver that calls the architecture default_idle routine.
>
> To be used in conjunction with the haltpoll governor.
>
> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
>
> ---
> arch/x86/kernel/process.c | 2 -
> drivers/cpuidle/Kconfig | 9 +++++
> drivers/cpuidle/Makefile | 1
> drivers/cpuidle/cpuidle-haltpoll.c | 65 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 76 insertions(+), 1 deletion(-)
>
> Index: linux-2.6.git/arch/x86/kernel/process.c
> ===================================================================
> --- linux-2.6.git.orig/arch/x86/kernel/process.c 2019-06-13 16:19:27.877064340 -0400
> +++ linux-2.6.git/arch/x86/kernel/process.c 2019-06-13 16:19:48.795544892 -0400
> @@ -580,7 +580,7 @@
> safe_halt();
> trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
> }
> -#ifdef CONFIG_APM_MODULE
> +#if defined(CONFIG_APM_MODULE) || defined(CONFIG_HALTPOLL_CPUIDLE_MODULE)
> EXPORT_SYMBOL(default_idle);
> #endif
>
> Index: linux-2.6.git/drivers/cpuidle/Kconfig
> ===================================================================
> --- linux-2.6.git.orig/drivers/cpuidle/Kconfig 2019-06-13 16:19:27.878064316 -0400
> +++ linux-2.6.git/drivers/cpuidle/Kconfig 2019-06-13 18:41:40.599912671 -0400
> @@ -51,6 +51,15 @@
> source "drivers/cpuidle/Kconfig.powerpc"
> endmenu
>
> +config HALTPOLL_CPUIDLE
> + tristate "Halt poll cpuidle driver"
> + depends on X86
> + default y
> + help
> + This option enables halt poll cpuidle driver, which allows to poll
> + before halting in the guest (more efficient than polling in the
> + host via halt_poll_ns for some scenarios).
> +
> endif
>
> config ARCH_NEEDS_CPU_IDLE_COUPLED
> Index: linux-2.6.git/drivers/cpuidle/Makefile
> ===================================================================
> --- linux-2.6.git.orig/drivers/cpuidle/Makefile 2019-06-13 16:19:27.878064316 -0400
> +++ linux-2.6.git/drivers/cpuidle/Makefile 2019-06-13 16:19:48.796544867 -0400
> @@ -7,6 +7,7 @@
> obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
> obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
> obj-$(CONFIG_ARCH_HAS_CPU_RELAX) += poll_state.o
> +obj-$(CONFIG_HALTPOLL_CPUIDLE) += cpuidle-haltpoll.o
>
> ##################################################################################
> # ARM SoC drivers
> Index: linux-2.6.git/drivers/cpuidle/cpuidle-haltpoll.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.git/drivers/cpuidle/cpuidle-haltpoll.c 2019-06-13 18:41:39.305933413 -0400
> @@ -0,0 +1,65 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * cpuidle driver for haltpoll governor.
> + *
> + * Copyright 2019 Red Hat, Inc. and/or its affiliates.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + *
> + * Authors: Marcelo Tosatti <mtosatti@redhat.com>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/cpuidle.h>
> +#include <linux/module.h>
> +#include <linux/sched/idle.h>
> +
> +static int default_enter_idle(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv, int index)
> +{
> + if (current_clr_polling_and_test()) {
> + local_irq_enable();
> + return index;
> + }
> + default_idle();
> + return index;
> +}
> +
> +static struct cpuidle_driver haltpoll_driver = {
> + .name = "haltpoll",
> + .owner = THIS_MODULE,
> + .states = {
> + { /* entry 0 is for polling */ },
> + {
> + .enter = default_enter_idle,
> + .exit_latency = 0,
> + .target_residency = 0,
> + .power_usage = -1,
> + .name = "haltpoll idle",
> + .desc = "default architecture idle",
> + },
> + },
> + .safe_state_index = 0,
> + .state_count = 2,
> +};
> +
> +static int __init haltpoll_init(void)
> +{
> + struct cpuidle_driver *drv = &haltpoll_driver;
> +
> + cpuidle_poll_state_init(drv);
> +
> + return cpuidle_register(&haltpoll_driver, NULL);
Would it be practical to prevent this driver from loading on non-virt?
> +}
> +
> +static void __exit haltpoll_exit(void)
> +{
> + cpuidle_unregister(&haltpoll_driver);
> +}
> +
> +module_init(haltpoll_init);
> +module_exit(haltpoll_exit);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
> +
Apart from the above it is simple enough.
^ permalink raw reply
* [PATCH v2 1/4] OPP: Allow required-opps even if the device doesn't have power-domains
From: Saravana Kannan @ 2019-06-25 21:33 UTC (permalink / raw)
To: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
Cc: Saravana Kannan, kernel-team, linux-pm, linux-kernel
In-Reply-To: <20190625213337.157525-1-saravanak@google.com>
A Device-A can have a (minimum) performance requirement on another
Device-B to be able to function correctly. This performance requirement
on Device-B can also change based on the current performance level of
Device-A.
The existing required-opps feature fits well to describe this need. So,
instead of limiting required-opps to point to only PM-domain devices,
allow it to point to any device.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/opp/core.c | 2 +-
drivers/opp/of.c | 14 --------------
2 files changed, 1 insertion(+), 15 deletions(-)
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 0e7703fe733f..74c7bdc6f463 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -710,7 +710,7 @@ static int _set_required_opps(struct device *dev,
return 0;
/* Single genpd case */
- if (!genpd_virt_devs) {
+ if (!genpd_virt_devs && required_opp_tables[0]->is_genpd) {
pstate = opp->required_opps[0]->pstate;
ret = dev_pm_genpd_set_performance_state(dev, pstate);
if (ret) {
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index c10c782d15aa..7c8336e94aff 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -195,9 +195,6 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
*/
count_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells");
- if (!count_pd)
- goto put_np;
-
if (count_pd > 1) {
genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
GFP_KERNEL);
@@ -226,17 +223,6 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
if (IS_ERR(required_opp_tables[i]))
goto free_required_tables;
-
- /*
- * We only support genpd's OPPs in the "required-opps" for now,
- * as we don't know how much about other cases. Error out if the
- * required OPP doesn't belong to a genpd.
- */
- if (!required_opp_tables[i]->is_genpd) {
- dev_err(dev, "required-opp doesn't belong to genpd: %pOF\n",
- required_np);
- goto free_required_tables;
- }
}
goto put_np;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v2 3/4] PM / devfreq: Cache OPP table reference in devfreq
From: Saravana Kannan @ 2019-06-25 21:33 UTC (permalink / raw)
To: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
Cc: Saravana Kannan, kernel-team, linux-pm, linux-kernel
In-Reply-To: <20190625213337.157525-1-saravanak@google.com>
The OPP table can be used often in devfreq. Trying to get it each time can
be expensive, so cache it in the devfreq struct.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/devfreq/devfreq.c | 6 ++++++
include/linux/devfreq.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 6b6991f0e873..ac62b78dc035 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -597,6 +597,8 @@ static void devfreq_dev_release(struct device *dev)
if (devfreq->profile->exit)
devfreq->profile->exit(devfreq->dev.parent);
+ if (devfreq->opp_table)
+ dev_pm_opp_put_opp_table(devfreq->opp_table);
mutex_destroy(&devfreq->lock);
kfree(devfreq);
}
@@ -677,6 +679,10 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->max_freq = devfreq->scaling_max_freq;
devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
+ devfreq->opp_table = dev_pm_opp_get_opp_table(dev);
+ if (IS_ERR(devfreq->opp_table))
+ devfreq->opp_table = NULL;
+
atomic_set(&devfreq->suspend_count, 0);
dev_set_name(&devfreq->dev, "devfreq%d",
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index fbffa74bfc1b..0d877c9513d7 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -156,6 +156,7 @@ struct devfreq {
struct devfreq_dev_profile *profile;
const struct devfreq_governor *governor;
char governor_name[DEVFREQ_NAME_LEN];
+ struct opp_table *opp_table;
struct notifier_block nb;
struct delayed_work work;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v2 4/4] PM / devfreq: Add required OPPs support to passive governor
From: Saravana Kannan @ 2019-06-25 21:33 UTC (permalink / raw)
To: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
Cc: Saravana Kannan, kernel-team, linux-pm, linux-kernel
In-Reply-To: <20190625213337.157525-1-saravanak@google.com>
Look at the required OPPs of the "parent" device to determine the OPP that
is required from the slave device managed by the passive governor. This
allows having mappings between a parent device and a slave device even when
they don't have the same number of OPPs.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/devfreq/governor_passive.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index 3bc29acbd54e..f6de03de7a64 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -22,7 +22,7 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
= (struct devfreq_passive_data *)devfreq->data;
struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
unsigned long child_freq = ULONG_MAX;
- struct dev_pm_opp *opp;
+ struct dev_pm_opp *opp = NULL, *p_opp = NULL;
int i, count, ret = 0;
/*
@@ -59,13 +59,20 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
* list of parent device. Because in this case, *freq is temporary
* value which is decided by ondemand governor.
*/
- opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0);
- if (IS_ERR(opp)) {
- ret = PTR_ERR(opp);
+ p_opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0);
+ if (IS_ERR(p_opp)) {
+ ret = PTR_ERR(p_opp);
goto out;
}
- dev_pm_opp_put(opp);
+ if (devfreq->opp_table && parent_devfreq->opp_table)
+ opp = dev_pm_opp_xlate_opp(parent_devfreq->opp_table,
+ devfreq->opp_table, p_opp);
+ if (opp) {
+ *freq = dev_pm_opp_get_freq(opp);
+ dev_pm_opp_put(opp);
+ goto out;
+ }
/*
* Get the OPP table's index of decided freqeuncy by governor
@@ -92,6 +99,9 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
*freq = child_freq;
out:
+ if (!IS_ERR_OR_NULL(opp))
+ dev_pm_opp_put(p_opp);
+
return ret;
}
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v2 2/4] OPP: Add function to look up required OPP's for a given OPP
From: Saravana Kannan @ 2019-06-25 21:33 UTC (permalink / raw)
To: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
Cc: Saravana Kannan, kernel-team, linux-pm, linux-kernel
In-Reply-To: <20190625213337.157525-1-saravanak@google.com>
Add a function that allows looking up required OPPs given a source OPP
table, destination OPP table and the source OPP.
Signed-off-by: Saravana Kannan <saravanak@google.com>
---
drivers/opp/core.c | 54 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_opp.h | 11 +++++++++
2 files changed, 65 insertions(+)
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 74c7bdc6f463..4f7870bffbf8 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1830,6 +1830,60 @@ void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
dev_err(virt_dev, "Failed to find required device entry\n");
}
+/**
+ * dev_pm_opp_xlate_opp() - Find required OPP for src_table OPP.
+ * @src_table: OPP table which has dst_table as one of its required OPP table.
+ * @dst_table: Required OPP table of the src_table.
+ * @pstate: OPP of the src_table.
+ *
+ * This function returns the OPP (present in @dst_table) pointed out by the
+ * "required-opps" property of the OPP (present in @src_table).
+ *
+ * The callers are required to call dev_pm_opp_put() for the returned OPP after
+ * use.
+ *
+ * Return: destination table OPP on success, otherwise NULL on errors.
+ */
+struct dev_pm_opp *dev_pm_opp_xlate_opp(struct opp_table *src_table,
+ struct opp_table *dst_table,
+ struct dev_pm_opp *src_opp)
+{
+ struct dev_pm_opp *opp, *dest_opp = NULL;
+ int i;
+
+ if (!src_table || !dst_table || !src_opp)
+ return NULL;
+
+ for (i = 0; i < src_table->required_opp_count; i++) {
+ if (src_table->required_opp_tables[i]->np == dst_table->np)
+ break;
+ }
+
+ if (unlikely(i == src_table->required_opp_count)) {
+ pr_err("%s: Couldn't find matching OPP table (%p: %p)\n",
+ __func__, src_table, dst_table);
+ return NULL;
+ }
+
+ mutex_lock(&src_table->lock);
+
+ list_for_each_entry(opp, &src_table->opp_list, node) {
+ if (opp == src_opp) {
+ dest_opp = opp->required_opps[i];
+ dev_pm_opp_get(dest_opp);
+ goto unlock;
+ }
+ }
+
+ pr_err("%s: Couldn't find matching OPP (%p: %p)\n", __func__, src_table,
+ dst_table);
+
+unlock:
+ mutex_unlock(&src_table->lock);
+
+ return dest_opp;
+}
+
/**
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
* @src_table: OPP table which has dst_table as one of its required OPP table.
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index b150fe97ce5a..bc5c68bdfc8d 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -134,6 +134,9 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
+struct dev_pm_opp *dev_pm_opp_xlate_opp(struct opp_table *src_table,
+ struct opp_table *dst_table,
+ struct dev_pm_opp *src_opp);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@@ -307,6 +310,14 @@ static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table
return -ENOTSUPP;
}
+static inline struct dev_pm_opp *dev_pm_opp_xlate_opp(
+ struct opp_table *src_table,
+ struct opp_table *dst_table,
+ struct dev_pm_opp *src_opp)
+{
+ return NULL;
+}
+
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
return -ENOTSUPP;
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v2 0/4] Add required-opps support to devfreq passive gov
From: Saravana Kannan @ 2019-06-25 21:33 UTC (permalink / raw)
To: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki
Cc: Saravana Kannan, kernel-team, linux-pm, linux-kernel
The devfreq passive governor scales the frequency of a "child" device based
on the current frequency of a "parent" device (not parent/child in the
sense of device hierarchy). As of today, the passive governor requires one
of the following to work correctly:
1. The parent and child device have the same number of frequencies
2. The child device driver passes a mapping function to translate from
parent frequency to child frequency.
When (1) is not true, (2) is the only option right now. But often times,
all that is required is a simple mapping from parent's frequency to child's
frequency.
Since OPPs already support pointing to other "required-opps", add support
for using that to map from parent device frequency to child device
frequency. That way, every child device driver doesn't have to implement a
separate mapping function anytime (1) isn't true.
Some common (but not comprehensive) reason for needing a devfreq passive
governor to adjust the frequency of one device based on another are:
1. These were the combination of frequencies that were validated/screened
during the manufacturing process.
2. These are the sensible performance combinations between two devices
interacting with each other. So that when one runs fast the other
doesn't become the bottleneck.
3. Hardware bugs requiring some kind of frequency ratio between devices.
For example, the following mapping can't be captured in DT as it stands
today because the parent and child device have different number of OPPs.
But with this patch series, this mapping can be captured cleanly.
In arch/arm64/boot/dts/exynos/exynos5433-bus.dtsi you have something
like this with the following changes:
bus_g2d_400: bus0 {
compatible = "samsung,exynos-bus";
clocks = <&cmu_top CLK_ACLK_G2D_400>;
clock-names = "bus";
operating-points-v2 = <&bus_g2d_400_opp_table>;
status = "disabled";
};
bus_noc2: bus9 {
compatible = "samsung,exynos-bus";
clocks = <&cmu_mif CLK_ACLK_BUS2_400>;
clock-names = "bus";
operating-points-v2 = <&bus_noc2_opp_table>;
status = "disabled";
};
bus_g2d_400_opp_table: opp_table2 {
compatible = "operating-points-v2";
opp-shared;
opp-400000000 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <1075000>;
required-opps = <&noc2_400>;
};
opp-267000000 {
opp-hz = /bits/ 64 <267000000>;
opp-microvolt = <1000000>;
required-opps = <&noc2_200>;
};
opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
opp-microvolt = <975000>;
required-opps = <&noc2_200>;
};
opp-160000000 {
opp-hz = /bits/ 64 <160000000>;
opp-microvolt = <962500>;
required-opps = <&noc2_134>;
};
opp-134000000 {
opp-hz = /bits/ 64 <134000000>;
opp-microvolt = <950000>;
required-opps = <&noc2_134>;
};
opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <937500>;
required-opps = <&noc2_100>;
};
};
bus_noc2_opp_table: opp_table6 {
compatible = "operating-points-v2";
noc2_400: opp-400000000 {
opp-hz = /bits/ 64 <400000000>;
};
noc2_200: opp-200000000 {
opp-hz = /bits/ 64 <200000000>;
};
noc2_134: opp-134000000 {
opp-hz = /bits/ 64 <134000000>;
};
noc2_100: opp-100000000 {
opp-hz = /bits/ 64 <100000000>;
};
};
-Saravana
v1 -> v2:
- Cached OPP table reference in devfreq to avoid looking up every time.
- Renamed variable in passive governor to be more intuitive.
- Updated cover letter with examples.
Saravana Kannan (4):
OPP: Allow required-opps even if the device doesn't have power-domains
OPP: Add function to look up required OPP's for a given OPP
PM / devfreq: Cache OPP table reference in devfreq
PM / devfreq: Add required OPPs support to passive governor
drivers/devfreq/devfreq.c | 6 ++++
drivers/devfreq/governor_passive.c | 20 ++++++++---
drivers/opp/core.c | 56 +++++++++++++++++++++++++++++-
drivers/opp/of.c | 14 --------
include/linux/devfreq.h | 1 +
include/linux/pm_opp.h | 11 ++++++
6 files changed, 88 insertions(+), 20 deletions(-)
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply
* Re: [PATCH v2] PCI: PM: Skip devices in D0 for suspend-to-idle
From: Rafael J. Wysocki @ 2019-06-25 16:23 UTC (permalink / raw)
To: Mika Westerberg
Cc: Jon Hunter, Linux PCI, Bjorn Helgaas, Linux PM, Linux ACPI, LKML,
Keith Busch, Kai-Heng Feng, linux-tegra
In-Reply-To: <2287147.DxjcvLeq6l@kreacher>
On Tue, Jun 25, 2019 at 1:09 AM Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
>
> On Tuesday, June 25, 2019 12:20:26 AM CEST Rafael J. Wysocki wrote:
> > On Mon, Jun 24, 2019 at 11:37 PM Rafael J. Wysocki <rafael@kernel.org> wrote:
> > >
> > > On Mon, Jun 24, 2019 at 2:43 PM Jon Hunter <jonathanh@nvidia.com> wrote:
> > > >
> > > > Hi Rafael,
> > > >
> > > > On 13/06/2019 22:59, Rafael J. Wysocki wrote:
> > > > > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > > > >
> > > > > Commit d491f2b75237 ("PCI: PM: Avoid possible suspend-to-idle issue")
> > > > > attempted to avoid a problem with devices whose drivers want them to
> > > > > stay in D0 over suspend-to-idle and resume, but it did not go as far
> > > > > as it should with that.
> > > > >
> > > > > Namely, first of all, the power state of a PCI bridge with a
> > > > > downstream device in D0 must be D0 (based on the PCI PM spec r1.2,
> > > > > sec 6, table 6-1, if the bridge is not in D0, there can be no PCI
> > > > > transactions on its secondary bus), but that is not actively enforced
> > > > > during system-wide PM transitions, so use the skip_bus_pm flag
> > > > > introduced by commit d491f2b75237 for that.
> > > > >
> > > > > Second, the configuration of devices left in D0 (whatever the reason)
> > > > > during suspend-to-idle need not be changed and attempting to put them
> > > > > into D0 again by force is pointless, so explicitly avoid doing that.
> > > > >
> > > > > Fixes: d491f2b75237 ("PCI: PM: Avoid possible suspend-to-idle issue")
> > > > > Reported-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
> > > > > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > > > > Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> > > > > Tested-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
> > > >
> > > > I have noticed a regression in both the mainline and -next branches on
> > > > one of our boards when testing suspend. The bisect is point to this
> > > > commit and reverting on top of mainline does fix the problem. So far I
> > > > have not looked at this in close detail but kernel log is showing ...
> > >
> > > Can you please collect a log like that, but with dynamic debug in
> > > pci-driver.c enabled?
> > >
> > > Note that reverting this commit is rather out of the question, so we
> > > need to get to the bottom of the failure.
> >
> > I suspect that there is a problem with the pm_suspend_via_firmware()
> > check which returns 'false' on the affected board, but the platform
> > actually removes power from devices left in D0 during suspend.
> >
> > I guess it would be more appropriate to check something like
> > pm_suspend_no_platform() which would return 'true' in the
> > suspend-to-idle patch w/ ACPI.
>
> So I wonder if the patch below makes any difference?
Mika, can you please test this one in combination with the other
changes we've been working on?
I really don't expect to see problems, but just to be sure ...
> ---
> drivers/pci/pci-driver.c | 8 ++++----
> include/linux/suspend.h | 26 ++++++++++++++++++++++++--
> kernel/power/suspend.c | 3 +++
> 3 files changed, 31 insertions(+), 6 deletions(-)
>
> Index: linux-pm/include/linux/suspend.h
> ===================================================================
> --- linux-pm.orig/include/linux/suspend.h
> +++ linux-pm/include/linux/suspend.h
> @@ -209,8 +209,9 @@ extern int suspend_valid_only_mem(suspen
>
> extern unsigned int pm_suspend_global_flags;
>
> -#define PM_SUSPEND_FLAG_FW_SUSPEND (1 << 0)
> -#define PM_SUSPEND_FLAG_FW_RESUME (1 << 1)
> +#define PM_SUSPEND_FLAG_FW_SUSPEND BIT(0)
> +#define PM_SUSPEND_FLAG_FW_RESUME BIT(1)
> +#define PM_SUSPEND_FLAG_NO_PLATFORM BIT(2)
>
> static inline void pm_suspend_clear_flags(void)
> {
> @@ -227,6 +228,11 @@ static inline void pm_set_resume_via_fir
> pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_RESUME;
> }
>
> +static inline void pm_set_suspend_no_platform(void)
> +{
> + pm_suspend_global_flags |= PM_SUSPEND_FLAG_NO_PLATFORM;
> +}
> +
> /**
> * pm_suspend_via_firmware - Check if platform firmware will suspend the system.
> *
> @@ -268,6 +274,22 @@ static inline bool pm_resume_via_firmwar
> return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_RESUME);
> }
>
> +/**
> + * pm_suspend_no_platform - Check if platform may change device power states.
> + *
> + * To be called during system-wide power management transitions to sleep states
> + * or during the subsequent system-wide transitions back to the working state.
> + *
> + * Return 'true' if the power states of devices remain under full control of the
> + * kernel throughout the system-wide suspend and resume cycle in progress (that
> + * is, if a device is put into a certain power state during suspend, it can be
> + * expected to remain in that state during resume).
> + */
> +static inline bool pm_suspend_no_platform(void)
> +{
> + return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_NO_PLATFORM);
> +}
> +
> /* Suspend-to-idle state machnine. */
> enum s2idle_states {
> S2IDLE_STATE_NONE, /* Not suspended/suspending. */
> Index: linux-pm/kernel/power/suspend.c
> ===================================================================
> --- linux-pm.orig/kernel/power/suspend.c
> +++ linux-pm/kernel/power/suspend.c
> @@ -493,6 +493,9 @@ int suspend_devices_and_enter(suspend_st
>
> pm_suspend_target_state = state;
>
> + if (state == PM_SUSPEND_TO_IDLE)
> + pm_set_suspend_no_platform();
> +
> error = platform_suspend_begin(state);
> if (error)
> goto Close;
> Index: linux-pm/drivers/pci/pci-driver.c
> ===================================================================
> --- linux-pm.orig/drivers/pci/pci-driver.c
> +++ linux-pm/drivers/pci/pci-driver.c
> @@ -870,7 +870,7 @@ static int pci_pm_suspend_noirq(struct d
> pci_dev->bus->self->skip_bus_pm = true;
> }
>
> - if (pci_dev->skip_bus_pm && !pm_suspend_via_firmware()) {
> + if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
> dev_dbg(dev, "PCI PM: Skipped\n");
> goto Fixup;
> }
> @@ -925,10 +925,10 @@ static int pci_pm_resume_noirq(struct de
> /*
> * In the suspend-to-idle case, devices left in D0 during suspend will
> * stay in D0, so it is not necessary to restore or update their
> - * configuration here and attempting to put them into D0 again may
> - * confuse some firmware, so avoid doing that.
> + * configuration here and attempting to put them into D0 again is
> + * pointless, so avoid doing that.
> */
> - if (!pci_dev->skip_bus_pm || pm_suspend_via_firmware())
> + if (!(pci_dev->skip_bus_pm && pm_suspend_no_platform()))
> pci_pm_default_resume_early(pci_dev);
>
> pci_fixup_device(pci_fixup_resume_early, pci_dev);
>
>
>
^ permalink raw reply
* Re: [PATCH] PCI: PM/ACPI: Refresh all stale power state data in pci_pm_complete()
From: Mika Westerberg @ 2019-06-25 16:09 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Linux PCI, Linux ACPI, Linux PM, LKML, Bjorn Helgaas,
Lukas Wunner
In-Reply-To: <6435024.1ODgWP2se3@kreacher>
On Tue, Jun 25, 2019 at 02:09:12PM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> In pci_pm_complete() there are checks to decide whether or not to
> resume devices that were left in runtime-suspend during the preceding
> system-wide transition into a sleep state. They involve checking the
> current power state of the device and comparing it with the power
> state of it set before the preceding system-wide transition, but the
> platform component of the device's power state is not handled
> correctly in there.
>
> Namely, on platforms with ACPI, the device power state information
> needs to be updated with care, so that the reference counters of
> power resources used by the device (if any) are set to ensure that
> the refreshed power state of it will be maintained going forward.
>
> To that end, introduce a new ->refresh_state() platform PM callback
> for PCI devices, for asking the platform to refresh the device power
> state data and ensure that the corresponding power state will be
> maintained going forward, make it invoke acpi_device_update_power()
> (for devices with ACPI PM) on platforms with ACPI and make
> pci_pm_complete() use it, through a new pci_refresh_power_state()
> wrapper function.
>
> Fixes: a0d2a959d3da (PCI: Avoid unnecessary resume after direct-complete)
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
I also tested this on Ice Lake system and did not see any issues over
several suspend-to-ram cycles.
^ permalink raw reply
* Re: [PATCH 12/13] int340X/processor_thermal_device: add support for MMIO RAPL
From: Zhang Rui @ 2019-06-25 15:52 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada
In-Reply-To: <1561475808-24839-13-git-send-email-rui.zhang@intel.com>
On 二, 2019-06-25 at 23:16 +0800, Zhang Rui wrote:
> Introduce MMIO RAPL support as Intel processor_thermal device exposes
> the
> capability to do RAPL control via MMIO registers.
>
> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
hmmm, just saw a build warning of this patch, I will check and resend.
thanks,
rui
> ---
> drivers/thermal/intel/int340x_thermal/Kconfig | 5 +
> .../int340x_thermal/processor_thermal_device.c | 174
> ++++++++++++++++++++-
> 2 files changed, 173 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig
> b/drivers/thermal/intel/int340x_thermal/Kconfig
> index 5333e01..547a35f 100644
> --- a/drivers/thermal/intel/int340x_thermal/Kconfig
> +++ b/drivers/thermal/intel/int340x_thermal/Kconfig
> @@ -40,4 +40,9 @@ config INT3406_THERMAL
> brightness in order to address a thermal condition or to
> reduce
> power consumed by display device.
>
> +config PROC_THERMAL_MMIO_RAPL
> + bool
> + depends on 64BIT
> + select INTEL_RAPL_CORE
> + default y
> endif
> diff --git
> a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
> b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
> index 53c84fa..5410c07 100644
> ---
> a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
> +++
> b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
> @@ -11,6 +11,8 @@
> #include <linux/platform_device.h>
> #include <linux/acpi.h>
> #include <linux/thermal.h>
> +#include <linux/cpuhotplug.h>
> +#include <linux/intel_rapl.h>
> #include "int340x_thermal_zone.h"
> #include "../intel_soc_dts_iosf.h"
>
> @@ -37,6 +39,8 @@
> /* GeminiLake thermal reporting device */
> #define PCI_DEVICE_ID_PROC_GLK_THERMAL 0x318C
>
> +#define DRV_NAME "proc_thermal"
> +
> struct power_config {
> u32 index;
> u32 min_uw;
> @@ -52,6 +56,7 @@ struct proc_thermal_device {
> struct power_config power_limits[2];
> struct int34x_thermal_zone *int340x_zone;
> struct intel_soc_dts_sensors *soc_dts;
> + void __iomem *mmio_base;
> };
>
> enum proc_thermal_emum_mode_type {
> @@ -60,6 +65,12 @@ enum proc_thermal_emum_mode_type {
> PROC_THERMAL_PLATFORM_DEV
> };
>
> +struct rapl_mmio_regs {
> + u64 reg_unit;
> + u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
> + int limits[RAPL_DOMAIN_MAX];
> +};
> +
> /*
> * We can have only one type of enumeration, PCI or Platform,
> * not both. So we don't need instance specific data.
> @@ -367,8 +378,152 @@ static irqreturn_t proc_thermal_pci_msi_irq(int
> irq, void *devid)
> return IRQ_HANDLED;
> }
>
> +#ifdef CONFIG_PROC_THERMAL_MMIO_RAPL
> +
> +#define MCHBAR 0
> +
> +/* RAPL Support via MMIO interface */
> +static struct rapl_priv rapl_mmio_priv;
> +
> +static int rapl_mmio_cpu_online(unsigned int cpu)
> +{
> + struct rapl_package *rp;
> +
> + /* mmio rapl supports package 0 only for now */
> + if (topology_physical_package_id(cpu))
> + return 0;
> +
> + rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
> + if (!rp) {
> + rp = rapl_add_package(cpu, &rapl_mmio_priv);
> + if (IS_ERR(rp))
> + return PTR_ERR(rp);
> + }
> + cpumask_set_cpu(cpu, &rp->cpumask);
> + return 0;
> +}
> +
> +static int rapl_mmio_cpu_down_prep(unsigned int cpu)
> +{
> + struct rapl_package *rp;
> + int lead_cpu;
> +
> + rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
> + if (!rp)
> + return 0;
> +
> + cpumask_clear_cpu(cpu, &rp->cpumask);
> + lead_cpu = cpumask_first(&rp->cpumask);
> + if (lead_cpu >= nr_cpu_ids)
> + rapl_remove_package(rp);
> + else if (rp->lead_cpu == cpu)
> + rp->lead_cpu = lead_cpu;
> + return 0;
> +}
> +
> +static int rapl_mmio_read_raw(int cpu, struct reg_action *ra)
> +{
> + if (!ra->reg)
> + return -EINVAL;
> +
> + ra->value = readq((void *)ra->reg);
> + ra->value &= ra->mask;
> + return 0;
> +}
> +
> +static int rapl_mmio_write_raw(int cpu, struct reg_action *ra)
> +{
> + u64 val;
> +
> + if (!ra->reg)
> + return -EINVAL;
> +
> + val = readq((void *)ra->reg);
> + val &= ~ra->mask;
> + val |= ra->value;
> + writeq(val, (void *)ra->reg);
> + return 0;
> +}
> +
> +static int proc_thermal_rapl_add(struct pci_dev *pdev,
> + struct proc_thermal_device
> *proc_priv,
> + struct rapl_mmio_regs *rapl_regs)
> +{
> + enum rapl_domain_reg_id reg;
> + enum rapl_domain_type domain;
> + u64 resource_start, resource_len;
> + int ret;
> +
> + if (!rapl_regs)
> + return 0;
> +
> + ret = pcim_iomap_regions(pdev, 1 << MCHBAR, DRV_NAME);
> + if (ret) {
> + dev_err(&pdev->dev, "cannot reserve PCI memory
> region\n");
> + return -ENOMEM;
> + }
> +
> + proc_priv->mmio_base = pcim_iomap_table(pdev)[MCHBAR];
> +
> + for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX;
> domain++) {
> + for (reg = RAPL_DOMAIN_REG_LIMIT; reg <
> RAPL_DOMAIN_REG_MAX; reg++)
> + if (rapl_regs->regs[domain][reg])
> + rapl_mmio_priv.regs[domain][reg] =
> + (u64)proc_priv-
> >mmio_base +
> + rapl_regs-
> >regs[domain][reg];
> + rapl_mmio_priv.limits[domain] = rapl_regs-
> >limits[domain];
> + }
> + rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base +
> rapl_regs->reg_unit;
> +
> + rapl_mmio_priv.read_raw = rapl_mmio_read_raw;
> + rapl_mmio_priv.write_raw = rapl_mmio_write_raw;
> +
> + rapl_mmio_priv.control_type =
> powercap_register_control_type(NULL, "intel-rapl-mmio", NULL);
> + if (IS_ERR(rapl_mmio_priv.control_type)) {
> + pr_debug("failed to register powercap
> control_type.\n");
> + return PTR_ERR(rapl_mmio_priv.control_type);
> + }
> +
> + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
> "powercap/rapl:online",
> + rapl_mmio_cpu_online,
> rapl_mmio_cpu_down_prep);
> + if (ret < 0) {
> + powercap_unregister_control_type(rapl_mmio_priv.cont
> rol_type);
> + return ret;
> + }
> + rapl_mmio_priv.pcap_rapl_online = ret;
> +
> + return 0;
> +}
> +
> +static void proc_thermal_rapl_remove(void)
> +{
> + cpuhp_remove_state(rapl_mmio_priv.pcap_rapl_online);
> + powercap_unregister_control_type(rapl_mmio_priv.control_type
> );
> +}
> +
> +static const struct rapl_mmio_regs rapl_mmio_hsw = {
> + .reg_unit = 0x5938,
> + .regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0,
> 0x5930},
> + .regs[RAPL_DOMAIN_DRAM] = { 0x58e0, 0x58e8, 0x58ec, 0, 0},
> + .limits[RAPL_DOMAIN_PACKAGE] = 2,
> + .limits[RAPL_DOMAIN_DRAM] = 2,
> +};
> +
> +#else
> +
> +static int proc_thermal_rapl_add(struct pci_dev *pdev,
> + struct proc_thermal_device
> *proc_priv,
> + struct rapl_mmio_regs *rapl_regs)
> +{
> + return 0;
> +}
> +static void proc_thermal_rapl_remove(void) {}
> +static const struct rapl_mmio_regs rapl_mmio_hsw;
> +
> +#endif /* CONFIG_MMIO_RAPL */
> +
> static int proc_thermal_pci_probe(struct pci_dev *pdev,
> - const struct pci_device_id
> *unused)
> + const struct pci_device_id *id)
> {
> struct proc_thermal_device *proc_priv;
> int ret;
> @@ -378,15 +533,21 @@ static int proc_thermal_pci_probe(struct
> pci_dev *pdev,
> return -ENODEV;
> }
>
> - ret = pci_enable_device(pdev);
> + ret = pcim_enable_device(pdev);
> if (ret < 0) {
> dev_err(&pdev->dev, "error: could not enable
> device\n");
> return ret;
> }
>
> ret = proc_thermal_add(&pdev->dev, &proc_priv);
> + if (ret)
> + return ret;
> +
> + ret = proc_thermal_rapl_add(pdev, proc_priv,
> + (struct rapl_mmio_regs *)id-
> >driver_data);
> if (ret) {
> - pci_disable_device(pdev);
> + dev_err(&pdev->dev, "failed to add RAPL MMIO
> interface\n");
> + proc_thermal_remove(proc_priv);
> return ret;
> }
>
> @@ -439,14 +600,15 @@ static void proc_thermal_pci_remove(struct
> pci_dev *pdev)
> pci_disable_msi(pdev);
> }
> }
> + proc_thermal_rapl_remove();
> proc_thermal_remove(proc_priv);
> - pci_disable_device(pdev);
> }
>
> static const struct pci_device_id proc_thermal_pci_ids[] = {
> { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_BDW_THERMAL)},
> { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_HSB_THERMAL)},
> - { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_SKL_THERMAL)},
> + { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_SKL_THERMAL),
> + .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
> { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_BSW_THERMAL)},
> { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_BXT0_THERMAL)},
> { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
> PCI_DEVICE_ID_PROC_BXT1_THERMAL)},
> @@ -461,7 +623,7 @@ static const struct pci_device_id
> proc_thermal_pci_ids[] = {
> MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
>
> static struct pci_driver proc_thermal_pci_driver = {
> - .name = "proc_thermal",
> + .name = DRV_NAME,
> .probe = proc_thermal_pci_probe,
> .remove = proc_thermal_pci_remove,
> .id_table = proc_thermal_pci_ids,
^ permalink raw reply
* [PATCH 09/13] intel_rapl: abstract RAPL common code
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
Split intel_rapl.c to intel_rapl_common.c and intel_rapl.c, where
intel_rapl_common.c contains the common code that can be used by both MSR
and MMIO interface.
intel_rapl.c still contains the implementation of RAPL MSR interface.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/Kconfig | 11 +-
drivers/powercap/Makefile | 1 +
drivers/powercap/intel_rapl.c | 1427 +--------------------------------
drivers/powercap/intel_rapl_common.c | 1468 ++++++++++++++++++++++++++++++++++
include/linux/intel_rapl.h | 7 +
5 files changed, 1491 insertions(+), 1423 deletions(-)
create mode 100644 drivers/powercap/intel_rapl_common.c
diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig
index 42d3798..dc1c138 100644
--- a/drivers/powercap/Kconfig
+++ b/drivers/powercap/Kconfig
@@ -16,14 +16,17 @@ menuconfig POWERCAP
if POWERCAP
# Client driver configurations go here.
+config INTEL_RAPL_CORE
+ tristate
+
config INTEL_RAPL
- tristate "Intel RAPL Support"
+ tristate "Intel RAPL Support via MSR Interface"
depends on X86 && IOSF_MBI
- default n
+ select INTEL_RAPL_CORE
---help---
This enables support for the Intel Running Average Power Limit (RAPL)
- technology which allows power limits to be enforced and monitored on
- modern Intel processors (Sandy Bridge and later).
+ technology via MSR interface, which allows power limits to be enforced
+ and monitored on modern Intel processors (Sandy Bridge and later).
In RAPL, the platform level settings are divided into domains for
fine grained control. These domains include processor package, DRAM
diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile
index 81c8cca..a692c6f 100644
--- a/drivers/powercap/Makefile
+++ b/drivers/powercap/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_POWERCAP) += powercap_sys.o
+obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o
obj-$(CONFIG_INTEL_RAPL) += intel_rapl.o
obj-$(CONFIG_IDLE_INJECT) += idle_inject.o
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index e476bd1..67130c4 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -28,53 +28,6 @@
/* Local defines */
#define MSR_PLATFORM_POWER_LIMIT 0x0000065C
-/* bitmasks for RAPL MSRs, used by primitive access functions */
-#define ENERGY_STATUS_MASK 0xffffffff
-
-#define POWER_LIMIT1_MASK 0x7FFF
-#define POWER_LIMIT1_ENABLE BIT(15)
-#define POWER_LIMIT1_CLAMP BIT(16)
-
-#define POWER_LIMIT2_MASK (0x7FFFULL<<32)
-#define POWER_LIMIT2_ENABLE BIT_ULL(47)
-#define POWER_LIMIT2_CLAMP BIT_ULL(48)
-#define POWER_PACKAGE_LOCK BIT_ULL(63)
-#define POWER_PP_LOCK BIT(31)
-
-#define TIME_WINDOW1_MASK (0x7FULL<<17)
-#define TIME_WINDOW2_MASK (0x7FULL<<49)
-
-#define POWER_UNIT_OFFSET 0
-#define POWER_UNIT_MASK 0x0F
-
-#define ENERGY_UNIT_OFFSET 0x08
-#define ENERGY_UNIT_MASK 0x1F00
-
-#define TIME_UNIT_OFFSET 0x10
-#define TIME_UNIT_MASK 0xF0000
-
-#define POWER_INFO_MAX_MASK (0x7fffULL<<32)
-#define POWER_INFO_MIN_MASK (0x7fffULL<<16)
-#define POWER_INFO_MAX_TIME_WIN_MASK (0x3fULL<<48)
-#define POWER_INFO_THERMAL_SPEC_MASK 0x7fff
-
-#define PERF_STATUS_THROTTLE_TIME_MASK 0xffffffff
-#define PP_POLICY_MASK 0x1F
-
-/* Non HW constants */
-#define RAPL_PRIMITIVE_DERIVED BIT(1) /* not from raw data */
-#define RAPL_PRIMITIVE_DUMMY BIT(2)
-
-#define TIME_WINDOW_MAX_MSEC 40000
-#define TIME_WINDOW_MIN_MSEC 250
-#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */
-enum unit_type {
- ARBITRARY_UNIT, /* no translation */
- POWER_UNIT,
- ENERGY_UNIT,
- TIME_UNIT,
-};
-
static struct rapl_priv rapl_msr_priv = {
.reg_unit = MSR_RAPL_POWER_UNIT,
.regs[RAPL_DOMAIN_PACKAGE] = {
@@ -89,1265 +42,6 @@ static struct rapl_priv rapl_msr_priv = {
MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0},
};
-/* per domain data, some are optional */
-#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
-
-#define DOMAIN_STATE_INACTIVE BIT(0)
-#define DOMAIN_STATE_POWER_LIMIT_SET BIT(1)
-#define DOMAIN_STATE_BIOS_LOCKED BIT(2)
-
-static const char pl1_name[] = "long_term";
-static const char pl2_name[] = "short_term";
-
-#define power_zone_to_rapl_domain(_zone) \
- container_of(_zone, struct rapl_domain, power_zone)
-
-struct rapl_defaults {
- u8 floor_freq_reg_addr;
- int (*check_unit)(struct rapl_package *rp, int cpu);
- void (*set_floor_freq)(struct rapl_domain *rd, bool mode);
- u64 (*compute_time_window)(struct rapl_package *rp, u64 val,
- bool to_raw);
- unsigned int dram_domain_energy_unit;
-};
-static struct rapl_defaults *rapl_defaults;
-
-/* Sideband MBI registers */
-#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2)
-#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf)
-
-#define PACKAGE_PLN_INT_SAVED BIT(0)
-#define MAX_PRIM_NAME (32)
-
-/* per domain data. used to describe individual knobs such that access function
- * can be consolidated into one instead of many inline functions.
- */
-struct rapl_primitive_info {
- const char *name;
- u64 mask;
- int shift;
- enum rapl_domain_reg_id id;
- enum unit_type unit;
- u32 flag;
-};
-
-#define PRIMITIVE_INFO_INIT(p, m, s, i, u, f) { \
- .name = #p, \
- .mask = m, \
- .shift = s, \
- .id = i, \
- .unit = u, \
- .flag = f \
- }
-
-static void rapl_init_domains(struct rapl_package *rp);
-static int rapl_read_data_raw(struct rapl_domain *rd,
- enum rapl_primitives prim,
- bool xlate, u64 *data);
-static int rapl_write_data_raw(struct rapl_domain *rd,
- enum rapl_primitives prim,
- unsigned long long value);
-static u64 rapl_unit_xlate(struct rapl_domain *rd,
- enum unit_type type, u64 value,
- int to_raw);
-static void package_power_limit_irq_save(struct rapl_package *rp);
-
-static LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */
-
-static const char * const rapl_domain_names[] = {
- "package",
- "core",
- "uncore",
- "dram",
- "psys",
-};
-
-/* caller to ensure CPU hotplug lock is held */
-static struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_priv *priv)
-{
- int id = topology_logical_die_id(cpu);
- struct rapl_package *rp;
-
- list_for_each_entry(rp, &rapl_packages, plist) {
- if (rp->id == id && rp->priv->control_type == priv->control_type)
- return rp;
- }
-
- return NULL;
-}
-
-static int get_energy_counter(struct powercap_zone *power_zone, u64 *energy_raw)
-{
- struct rapl_domain *rd;
- u64 energy_now;
-
- /* prevent CPU hotplug, make sure the RAPL domain does not go
- * away while reading the counter.
- */
- get_online_cpus();
- rd = power_zone_to_rapl_domain(power_zone);
-
- if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now)) {
- *energy_raw = energy_now;
- put_online_cpus();
-
- return 0;
- }
- put_online_cpus();
-
- return -EIO;
-}
-
-static int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy)
-{
- struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev);
-
- *energy = rapl_unit_xlate(rd, ENERGY_UNIT, ENERGY_STATUS_MASK, 0);
- return 0;
-}
-
-static int release_zone(struct powercap_zone *power_zone)
-{
- struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone);
- struct rapl_package *rp = rd->rp;
-
- /* package zone is the last zone of a package, we can free
- * memory here since all children has been unregistered.
- */
- if (rd->id == RAPL_DOMAIN_PACKAGE) {
- kfree(rd);
- rp->domains = NULL;
- }
-
- return 0;
-
-}
-
-static int find_nr_power_limit(struct rapl_domain *rd)
-{
- int i, nr_pl = 0;
-
- for (i = 0; i < NR_POWER_LIMITS; i++) {
- if (rd->rpl[i].name)
- nr_pl++;
- }
-
- return nr_pl;
-}
-
-static int set_domain_enable(struct powercap_zone *power_zone, bool mode)
-{
- struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone);
-
- if (rd->state & DOMAIN_STATE_BIOS_LOCKED)
- return -EACCES;
-
- get_online_cpus();
- rapl_write_data_raw(rd, PL1_ENABLE, mode);
- if (rapl_defaults->set_floor_freq)
- rapl_defaults->set_floor_freq(rd, mode);
- put_online_cpus();
-
- return 0;
-}
-
-static int get_domain_enable(struct powercap_zone *power_zone, bool *mode)
-{
- struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone);
- u64 val;
-
- if (rd->state & DOMAIN_STATE_BIOS_LOCKED) {
- *mode = false;
- return 0;
- }
- get_online_cpus();
- if (rapl_read_data_raw(rd, PL1_ENABLE, true, &val)) {
- put_online_cpus();
- return -EIO;
- }
- *mode = val;
- put_online_cpus();
-
- return 0;
-}
-
-/* per RAPL domain ops, in the order of rapl_domain_type */
-static const struct powercap_zone_ops zone_ops[] = {
- /* RAPL_DOMAIN_PACKAGE */
- {
- .get_energy_uj = get_energy_counter,
- .get_max_energy_range_uj = get_max_energy_counter,
- .release = release_zone,
- .set_enable = set_domain_enable,
- .get_enable = get_domain_enable,
- },
- /* RAPL_DOMAIN_PP0 */
- {
- .get_energy_uj = get_energy_counter,
- .get_max_energy_range_uj = get_max_energy_counter,
- .release = release_zone,
- .set_enable = set_domain_enable,
- .get_enable = get_domain_enable,
- },
- /* RAPL_DOMAIN_PP1 */
- {
- .get_energy_uj = get_energy_counter,
- .get_max_energy_range_uj = get_max_energy_counter,
- .release = release_zone,
- .set_enable = set_domain_enable,
- .get_enable = get_domain_enable,
- },
- /* RAPL_DOMAIN_DRAM */
- {
- .get_energy_uj = get_energy_counter,
- .get_max_energy_range_uj = get_max_energy_counter,
- .release = release_zone,
- .set_enable = set_domain_enable,
- .get_enable = get_domain_enable,
- },
- /* RAPL_DOMAIN_PLATFORM */
- {
- .get_energy_uj = get_energy_counter,
- .get_max_energy_range_uj = get_max_energy_counter,
- .release = release_zone,
- .set_enable = set_domain_enable,
- .get_enable = get_domain_enable,
- },
-};
-
-
-/*
- * Constraint index used by powercap can be different than power limit (PL)
- * index in that some PLs maybe missing due to non-existant MSRs. So we
- * need to convert here by finding the valid PLs only (name populated).
- */
-static int contraint_to_pl(struct rapl_domain *rd, int cid)
-{
- int i, j;
-
- for (i = 0, j = 0; i < NR_POWER_LIMITS; i++) {
- if ((rd->rpl[i].name) && j++ == cid) {
- pr_debug("%s: index %d\n", __func__, i);
- return i;
- }
- }
- pr_err("Cannot find matching power limit for constraint %d\n", cid);
-
- return -EINVAL;
-}
-
-static int set_power_limit(struct powercap_zone *power_zone, int cid,
- u64 power_limit)
-{
- struct rapl_domain *rd;
- struct rapl_package *rp;
- int ret = 0;
- int id;
-
- get_online_cpus();
- rd = power_zone_to_rapl_domain(power_zone);
- id = contraint_to_pl(rd, cid);
- if (id < 0) {
- ret = id;
- goto set_exit;
- }
-
- rp = rd->rp;
-
- if (rd->state & DOMAIN_STATE_BIOS_LOCKED) {
- dev_warn(&power_zone->dev, "%s locked by BIOS, monitoring only\n",
- rd->name);
- ret = -EACCES;
- goto set_exit;
- }
-
- switch (rd->rpl[id].prim_id) {
- case PL1_ENABLE:
- rapl_write_data_raw(rd, POWER_LIMIT1, power_limit);
- break;
- case PL2_ENABLE:
- rapl_write_data_raw(rd, POWER_LIMIT2, power_limit);
- break;
- default:
- ret = -EINVAL;
- }
- if (!ret)
- package_power_limit_irq_save(rp);
-set_exit:
- put_online_cpus();
- return ret;
-}
-
-static int get_current_power_limit(struct powercap_zone *power_zone, int cid,
- u64 *data)
-{
- struct rapl_domain *rd;
- u64 val;
- int prim;
- int ret = 0;
- int id;
-
- get_online_cpus();
- rd = power_zone_to_rapl_domain(power_zone);
- id = contraint_to_pl(rd, cid);
- if (id < 0) {
- ret = id;
- goto get_exit;
- }
-
- switch (rd->rpl[id].prim_id) {
- case PL1_ENABLE:
- prim = POWER_LIMIT1;
- break;
- case PL2_ENABLE:
- prim = POWER_LIMIT2;
- break;
- default:
- put_online_cpus();
- return -EINVAL;
- }
- if (rapl_read_data_raw(rd, prim, true, &val))
- ret = -EIO;
- else
- *data = val;
-
-get_exit:
- put_online_cpus();
-
- return ret;
-}
-
-static int set_time_window(struct powercap_zone *power_zone, int cid,
- u64 window)
-{
- struct rapl_domain *rd;
- int ret = 0;
- int id;
-
- get_online_cpus();
- rd = power_zone_to_rapl_domain(power_zone);
- id = contraint_to_pl(rd, cid);
- if (id < 0) {
- ret = id;
- goto set_time_exit;
- }
-
- switch (rd->rpl[id].prim_id) {
- case PL1_ENABLE:
- rapl_write_data_raw(rd, TIME_WINDOW1, window);
- break;
- case PL2_ENABLE:
- rapl_write_data_raw(rd, TIME_WINDOW2, window);
- break;
- default:
- ret = -EINVAL;
- }
-
-set_time_exit:
- put_online_cpus();
- return ret;
-}
-
-static int get_time_window(struct powercap_zone *power_zone, int cid, u64 *data)
-{
- struct rapl_domain *rd;
- u64 val;
- int ret = 0;
- int id;
-
- get_online_cpus();
- rd = power_zone_to_rapl_domain(power_zone);
- id = contraint_to_pl(rd, cid);
- if (id < 0) {
- ret = id;
- goto get_time_exit;
- }
-
- switch (rd->rpl[id].prim_id) {
- case PL1_ENABLE:
- ret = rapl_read_data_raw(rd, TIME_WINDOW1, true, &val);
- break;
- case PL2_ENABLE:
- ret = rapl_read_data_raw(rd, TIME_WINDOW2, true, &val);
- break;
- default:
- put_online_cpus();
- return -EINVAL;
- }
- if (!ret)
- *data = val;
-
-get_time_exit:
- put_online_cpus();
-
- return ret;
-}
-
-static const char *get_constraint_name(struct powercap_zone *power_zone, int cid)
-{
- struct rapl_domain *rd;
- int id;
-
- rd = power_zone_to_rapl_domain(power_zone);
- id = contraint_to_pl(rd, cid);
- if (id >= 0)
- return rd->rpl[id].name;
-
- return NULL;
-}
-
-
-static int get_max_power(struct powercap_zone *power_zone, int id,
- u64 *data)
-{
- struct rapl_domain *rd;
- u64 val;
- int prim;
- int ret = 0;
-
- get_online_cpus();
- rd = power_zone_to_rapl_domain(power_zone);
- switch (rd->rpl[id].prim_id) {
- case PL1_ENABLE:
- prim = THERMAL_SPEC_POWER;
- break;
- case PL2_ENABLE:
- prim = MAX_POWER;
- break;
- default:
- put_online_cpus();
- return -EINVAL;
- }
- if (rapl_read_data_raw(rd, prim, true, &val))
- ret = -EIO;
- else
- *data = val;
-
- put_online_cpus();
-
- return ret;
-}
-
-static const struct powercap_zone_constraint_ops constraint_ops = {
- .set_power_limit_uw = set_power_limit,
- .get_power_limit_uw = get_current_power_limit,
- .set_time_window_us = set_time_window,
- .get_time_window_us = get_time_window,
- .get_max_power_uw = get_max_power,
- .get_name = get_constraint_name,
-};
-
-/* called after domain detection and package level data are set */
-static void rapl_init_domains(struct rapl_package *rp)
-{
- int i;
- struct rapl_domain *rd = rp->domains;
-
- for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
- unsigned int mask = rp->domain_map & (1 << i);
-
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = rp->priv->regs[i][RAPL_DOMAIN_REG_LIMIT];
- rd->regs[RAPL_DOMAIN_REG_STATUS] = rp->priv->regs[i][RAPL_DOMAIN_REG_STATUS];
- rd->regs[RAPL_DOMAIN_REG_PERF] = rp->priv->regs[i][RAPL_DOMAIN_REG_PERF];
- rd->regs[RAPL_DOMAIN_REG_POLICY] = rp->priv->regs[i][RAPL_DOMAIN_REG_POLICY];
- rd->regs[RAPL_DOMAIN_REG_INFO] = rp->priv->regs[i][RAPL_DOMAIN_REG_INFO];
-
- switch (mask) {
- case BIT(RAPL_DOMAIN_PACKAGE):
- rd->name = rapl_domain_names[RAPL_DOMAIN_PACKAGE];
- rd->id = RAPL_DOMAIN_PACKAGE;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- rd->rpl[1].prim_id = PL2_ENABLE;
- rd->rpl[1].name = pl2_name;
- break;
- case BIT(RAPL_DOMAIN_PP0):
- rd->name = rapl_domain_names[RAPL_DOMAIN_PP0];
- rd->id = RAPL_DOMAIN_PP0;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- break;
- case BIT(RAPL_DOMAIN_PP1):
- rd->name = rapl_domain_names[RAPL_DOMAIN_PP1];
- rd->id = RAPL_DOMAIN_PP1;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- break;
- case BIT(RAPL_DOMAIN_DRAM):
- rd->name = rapl_domain_names[RAPL_DOMAIN_DRAM];
- rd->id = RAPL_DOMAIN_DRAM;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- rd->domain_energy_unit =
- rapl_defaults->dram_domain_energy_unit;
- if (rd->domain_energy_unit)
- pr_info("DRAM domain energy unit %dpj\n",
- rd->domain_energy_unit);
- break;
- }
- if (mask) {
- rd->rp = rp;
- rd++;
- }
- }
-}
-
-static u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type,
- u64 value, int to_raw)
-{
- u64 units = 1;
- struct rapl_package *rp = rd->rp;
- u64 scale = 1;
-
- switch (type) {
- case POWER_UNIT:
- units = rp->power_unit;
- break;
- case ENERGY_UNIT:
- scale = ENERGY_UNIT_SCALE;
- /* per domain unit takes precedence */
- if (rd->domain_energy_unit)
- units = rd->domain_energy_unit;
- else
- units = rp->energy_unit;
- break;
- case TIME_UNIT:
- return rapl_defaults->compute_time_window(rp, value, to_raw);
- case ARBITRARY_UNIT:
- default:
- return value;
- };
-
- if (to_raw)
- return div64_u64(value, units) * scale;
-
- value *= units;
-
- return div64_u64(value, scale);
-}
-
-/* in the order of enum rapl_primitives */
-static struct rapl_primitive_info rpi[] = {
- /* name, mask, shift, msr index, unit divisor */
- PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0,
- RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0),
- PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0,
- RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
- PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32,
- RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
- PRIMITIVE_INFO_INIT(FW_LOCK, POWER_PP_LOCK, 31,
- RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
- PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15,
- RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
- PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16,
- RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
- PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47,
- RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
- PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48,
- RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
- PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17,
- RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
- PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49,
- RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
- PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK,
- 0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
- PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32,
- RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
- PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16,
- RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
- PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48,
- RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0),
- PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0,
- RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0),
- PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0,
- RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0),
- /* non-hardware */
- PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT,
- RAPL_PRIMITIVE_DERIVED),
- {NULL, 0, 0, 0},
-};
-
-/* Read primitive data based on its related struct rapl_primitive_info.
- * if xlate flag is set, return translated data based on data units, i.e.
- * time, energy, and power.
- * RAPL MSRs are non-architectual and are laid out not consistently across
- * domains. Here we use primitive info to allow writing consolidated access
- * functions.
- * For a given primitive, it is processed by MSR mask and shift. Unit conversion
- * is pre-assigned based on RAPL unit MSRs read at init time.
- * 63-------------------------- 31--------------------------- 0
- * | xxxxx (mask) |
- * | |<- shift ----------------|
- * 63-------------------------- 31--------------------------- 0
- */
-static int rapl_read_data_raw(struct rapl_domain *rd,
- enum rapl_primitives prim,
- bool xlate, u64 *data)
-{
- u64 value;
- struct rapl_primitive_info *rp = &rpi[prim];
- struct reg_action ra;
- int cpu;
-
- if (!rp->name || rp->flag & RAPL_PRIMITIVE_DUMMY)
- return -EINVAL;
-
- ra.reg = rd->regs[rp->id];
- if (!ra.reg)
- return -EINVAL;
-
- cpu = rd->rp->lead_cpu;
-
- /* special-case package domain, which uses a different bit*/
- if (prim == FW_LOCK && rd->id == RAPL_DOMAIN_PACKAGE) {
- rp->mask = POWER_PACKAGE_LOCK;
- rp->shift = 63;
- }
- /* non-hardware data are collected by the polling thread */
- if (rp->flag & RAPL_PRIMITIVE_DERIVED) {
- *data = rd->rdd.primitives[prim];
- return 0;
- }
-
- ra.mask = rp->mask;
-
- if (rd->rp->priv->read_raw(cpu, &ra)) {
- pr_debug("failed to read reg 0x%x on cpu %d\n", ra.reg, cpu);
- return -EIO;
- }
-
- value = ra.value >> rp->shift;
-
- if (xlate)
- *data = rapl_unit_xlate(rd, rp->unit, value, 0);
- else
- *data = value;
-
- return 0;
-}
-
-/* Similar use of primitive info in the read counterpart */
-static int rapl_write_data_raw(struct rapl_domain *rd,
- enum rapl_primitives prim,
- unsigned long long value)
-{
- struct rapl_primitive_info *rp = &rpi[prim];
- int cpu;
- u64 bits;
- struct reg_action ra;
- int ret;
-
- cpu = rd->rp->lead_cpu;
- bits = rapl_unit_xlate(rd, rp->unit, value, 1);
- bits <<= rp->shift;
- bits &= rp->mask;
-
- memset(&ra, 0, sizeof(ra));
-
- ra.reg = rd->regs[rp->id];
- ra.mask = rp->mask;
- ra.value = bits;
-
- ret = rd->rp->priv->write_raw(cpu, &ra);
-
- return ret;
-}
-
-/*
- * Raw RAPL data stored in MSRs are in certain scales. We need to
- * convert them into standard units based on the units reported in
- * the RAPL unit MSRs. This is specific to CPUs as the method to
- * calculate units differ on different CPUs.
- * We convert the units to below format based on CPUs.
- * i.e.
- * energy unit: picoJoules : Represented in picoJoules by default
- * power unit : microWatts : Represented in milliWatts by default
- * time unit : microseconds: Represented in seconds by default
- */
-static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
-{
- struct reg_action ra;
- u32 value;
-
- ra.reg = rp->priv->reg_unit;
- ra.mask = ~0;
- if (rp->priv->read_raw(cpu, &ra)) {
- pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
- rp->priv->reg_unit, cpu);
- return -ENODEV;
- }
-
- value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
- rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value);
-
- value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
- rp->power_unit = 1000000 / (1 << value);
-
- value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
- rp->time_unit = 1000000 / (1 << value);
-
- pr_debug("Core CPU %s energy=%dpJ, time=%dus, power=%duW\n",
- rp->name, rp->energy_unit, rp->time_unit, rp->power_unit);
-
- return 0;
-}
-
-static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
-{
- struct reg_action ra;
- u32 value;
-
- ra.reg = rp->priv->reg_unit;
- ra.mask = ~0;
- if (rp->priv->read_raw(cpu, &ra)) {
- pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
- rp->priv->reg_unit, cpu);
- return -ENODEV;
- }
-
- value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
- rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value;
-
- value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
- rp->power_unit = (1 << value) * 1000;
-
- value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
- rp->time_unit = 1000000 / (1 << value);
-
- pr_debug("Atom %s energy=%dpJ, time=%dus, power=%duW\n",
- rp->name, rp->energy_unit, rp->time_unit, rp->power_unit);
-
- return 0;
-}
-
-static void power_limit_irq_save_cpu(void *info)
-{
- u32 l, h = 0;
- struct rapl_package *rp = (struct rapl_package *)info;
-
- /* save the state of PLN irq mask bit before disabling it */
- rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h);
- if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED)) {
- rp->power_limit_irq = l & PACKAGE_THERM_INT_PLN_ENABLE;
- rp->power_limit_irq |= PACKAGE_PLN_INT_SAVED;
- }
- l &= ~PACKAGE_THERM_INT_PLN_ENABLE;
- wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
-}
-
-
-/* REVISIT:
- * When package power limit is set artificially low by RAPL, LVT
- * thermal interrupt for package power limit should be ignored
- * since we are not really exceeding the real limit. The intention
- * is to avoid excessive interrupts while we are trying to save power.
- * A useful feature might be routing the package_power_limit interrupt
- * to userspace via eventfd. once we have a usecase, this is simple
- * to do by adding an atomic notifier.
- */
-
-static void package_power_limit_irq_save(struct rapl_package *rp)
-{
- if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN))
- return;
-
- smp_call_function_single(rp->lead_cpu, power_limit_irq_save_cpu, rp, 1);
-}
-
-/*
- * Restore per package power limit interrupt enable state. Called from cpu
- * hotplug code on package removal.
- */
-static void package_power_limit_irq_restore(struct rapl_package *rp)
-{
- u32 l, h;
-
- if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN))
- return;
-
- /* irq enable state not saved, nothing to restore */
- if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED))
- return;
-
- rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h);
-
- if (rp->power_limit_irq & PACKAGE_THERM_INT_PLN_ENABLE)
- l |= PACKAGE_THERM_INT_PLN_ENABLE;
- else
- l &= ~PACKAGE_THERM_INT_PLN_ENABLE;
-
- wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
-}
-
-static void set_floor_freq_default(struct rapl_domain *rd, bool mode)
-{
- int nr_powerlimit = find_nr_power_limit(rd);
-
- /* always enable clamp such that p-state can go below OS requested
- * range. power capping priority over guranteed frequency.
- */
- rapl_write_data_raw(rd, PL1_CLAMP, mode);
-
- /* some domains have pl2 */
- if (nr_powerlimit > 1) {
- rapl_write_data_raw(rd, PL2_ENABLE, mode);
- rapl_write_data_raw(rd, PL2_CLAMP, mode);
- }
-}
-
-static void set_floor_freq_atom(struct rapl_domain *rd, bool enable)
-{
- static u32 power_ctrl_orig_val;
- u32 mdata;
-
- if (!rapl_defaults->floor_freq_reg_addr) {
- pr_err("Invalid floor frequency config register\n");
- return;
- }
-
- if (!power_ctrl_orig_val)
- iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_CR_READ,
- rapl_defaults->floor_freq_reg_addr,
- &power_ctrl_orig_val);
- mdata = power_ctrl_orig_val;
- if (enable) {
- mdata &= ~(0x7f << 8);
- mdata |= 1 << 8;
- }
- iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_CR_WRITE,
- rapl_defaults->floor_freq_reg_addr, mdata);
-}
-
-static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
- bool to_raw)
-{
- u64 f, y; /* fraction and exp. used for time unit */
-
- /*
- * Special processing based on 2^Y*(1+F/4), refer
- * to Intel Software Developer's manual Vol.3B: CH 14.9.3.
- */
- if (!to_raw) {
- f = (value & 0x60) >> 5;
- y = value & 0x1f;
- value = (1 << y) * (4 + f) * rp->time_unit / 4;
- } else {
- do_div(value, rp->time_unit);
- y = ilog2(value);
- f = div64_u64(4 * (value - (1 << y)), 1 << y);
- value = (y & 0x1f) | ((f & 0x3) << 5);
- }
- return value;
-}
-
-static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
- bool to_raw)
-{
- /*
- * Atom time unit encoding is straight forward val * time_unit,
- * where time_unit is default to 1 sec. Never 0.
- */
- if (!to_raw)
- return (value) ? value *= rp->time_unit : rp->time_unit;
- else
- value = div64_u64(value, rp->time_unit);
-
- return value;
-}
-
-static const struct rapl_defaults rapl_defaults_core = {
- .floor_freq_reg_addr = 0,
- .check_unit = rapl_check_unit_core,
- .set_floor_freq = set_floor_freq_default,
- .compute_time_window = rapl_compute_time_window_core,
-};
-
-static const struct rapl_defaults rapl_defaults_hsw_server = {
- .check_unit = rapl_check_unit_core,
- .set_floor_freq = set_floor_freq_default,
- .compute_time_window = rapl_compute_time_window_core,
- .dram_domain_energy_unit = 15300,
-};
-
-static const struct rapl_defaults rapl_defaults_byt = {
- .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT,
- .check_unit = rapl_check_unit_atom,
- .set_floor_freq = set_floor_freq_atom,
- .compute_time_window = rapl_compute_time_window_atom,
-};
-
-static const struct rapl_defaults rapl_defaults_tng = {
- .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG,
- .check_unit = rapl_check_unit_atom,
- .set_floor_freq = set_floor_freq_atom,
- .compute_time_window = rapl_compute_time_window_atom,
-};
-
-static const struct rapl_defaults rapl_defaults_ann = {
- .floor_freq_reg_addr = 0,
- .check_unit = rapl_check_unit_atom,
- .set_floor_freq = NULL,
- .compute_time_window = rapl_compute_time_window_atom,
-};
-
-static const struct rapl_defaults rapl_defaults_cht = {
- .floor_freq_reg_addr = 0,
- .check_unit = rapl_check_unit_atom,
- .set_floor_freq = NULL,
- .compute_time_window = rapl_compute_time_window_atom,
-};
-
-static const struct x86_cpu_id rapl_ids[] __initconst = {
- INTEL_CPU_FAM6(SANDYBRIDGE, rapl_defaults_core),
- INTEL_CPU_FAM6(SANDYBRIDGE_X, rapl_defaults_core),
-
- INTEL_CPU_FAM6(IVYBRIDGE, rapl_defaults_core),
- INTEL_CPU_FAM6(IVYBRIDGE_X, rapl_defaults_core),
-
- INTEL_CPU_FAM6(HASWELL_CORE, rapl_defaults_core),
- INTEL_CPU_FAM6(HASWELL_ULT, rapl_defaults_core),
- INTEL_CPU_FAM6(HASWELL_GT3E, rapl_defaults_core),
- INTEL_CPU_FAM6(HASWELL_X, rapl_defaults_hsw_server),
-
- INTEL_CPU_FAM6(BROADWELL_CORE, rapl_defaults_core),
- INTEL_CPU_FAM6(BROADWELL_GT3E, rapl_defaults_core),
- INTEL_CPU_FAM6(BROADWELL_XEON_D, rapl_defaults_core),
- INTEL_CPU_FAM6(BROADWELL_X, rapl_defaults_hsw_server),
-
- INTEL_CPU_FAM6(SKYLAKE_DESKTOP, rapl_defaults_core),
- INTEL_CPU_FAM6(SKYLAKE_MOBILE, rapl_defaults_core),
- INTEL_CPU_FAM6(SKYLAKE_X, rapl_defaults_hsw_server),
- INTEL_CPU_FAM6(KABYLAKE_MOBILE, rapl_defaults_core),
- INTEL_CPU_FAM6(KABYLAKE_DESKTOP, rapl_defaults_core),
- INTEL_CPU_FAM6(CANNONLAKE_MOBILE, rapl_defaults_core),
- INTEL_CPU_FAM6(ICELAKE_MOBILE, rapl_defaults_core),
-
- INTEL_CPU_FAM6(ATOM_SILVERMONT, rapl_defaults_byt),
- INTEL_CPU_FAM6(ATOM_AIRMONT, rapl_defaults_cht),
- INTEL_CPU_FAM6(ATOM_SILVERMONT_MID, rapl_defaults_tng),
- INTEL_CPU_FAM6(ATOM_AIRMONT_MID, rapl_defaults_ann),
- INTEL_CPU_FAM6(ATOM_GOLDMONT, rapl_defaults_core),
- INTEL_CPU_FAM6(ATOM_GOLDMONT_PLUS, rapl_defaults_core),
- INTEL_CPU_FAM6(ATOM_GOLDMONT_X, rapl_defaults_core),
- INTEL_CPU_FAM6(ATOM_TREMONT_X, rapl_defaults_core),
-
- INTEL_CPU_FAM6(XEON_PHI_KNL, rapl_defaults_hsw_server),
- INTEL_CPU_FAM6(XEON_PHI_KNM, rapl_defaults_hsw_server),
- {}
-};
-MODULE_DEVICE_TABLE(x86cpu, rapl_ids);
-
-/* Read once for all raw primitive data for domains */
-static void rapl_update_domain_data(struct rapl_package *rp)
-{
- int dmn, prim;
- u64 val;
-
- for (dmn = 0; dmn < rp->nr_domains; dmn++) {
- pr_debug("update %s domain %s data\n", rp->name,
- rp->domains[dmn].name);
- /* exclude non-raw primitives */
- for (prim = 0; prim < NR_RAW_PRIMITIVES; prim++) {
- if (!rapl_read_data_raw(&rp->domains[dmn], prim,
- rpi[prim].unit, &val))
- rp->domains[dmn].rdd.primitives[prim] = val;
- }
- }
-
-}
-
-static int rapl_package_register_powercap(struct rapl_package *rp)
-{
- struct rapl_domain *rd;
- struct powercap_zone *power_zone = NULL;
- int nr_pl, ret;
-
- /* Update the domain data of the new package */
- rapl_update_domain_data(rp);
-
- /* first we register package domain as the parent zone*/
- for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
- if (rd->id == RAPL_DOMAIN_PACKAGE) {
- nr_pl = find_nr_power_limit(rd);
- pr_debug("register package domain %s\n", rp->name);
- power_zone = powercap_register_zone(&rd->power_zone,
- rp->priv->control_type,
- rp->name, NULL,
- &zone_ops[rd->id],
- nr_pl,
- &constraint_ops);
- if (IS_ERR(power_zone)) {
- pr_debug("failed to register power zone %s\n",
- rp->name);
- return PTR_ERR(power_zone);
- }
- /* track parent zone in per package/socket data */
- rp->power_zone = power_zone;
- /* done, only one package domain per socket */
- break;
- }
- }
- if (!power_zone) {
- pr_err("no package domain found, unknown topology!\n");
- return -ENODEV;
- }
- /* now register domains as children of the socket/package*/
- for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
- if (rd->id == RAPL_DOMAIN_PACKAGE)
- continue;
- /* number of power limits per domain varies */
- nr_pl = find_nr_power_limit(rd);
- power_zone = powercap_register_zone(&rd->power_zone,
- rp->priv->control_type, rd->name,
- rp->power_zone,
- &zone_ops[rd->id], nr_pl,
- &constraint_ops);
-
- if (IS_ERR(power_zone)) {
- pr_debug("failed to register power_zone, %s:%s\n",
- rp->name, rd->name);
- ret = PTR_ERR(power_zone);
- goto err_cleanup;
- }
- }
- return 0;
-
-err_cleanup:
- /*
- * Clean up previously initialized domains within the package if we
- * failed after the first domain setup.
- */
- while (--rd >= rp->domains) {
- pr_debug("unregister %s domain %s\n", rp->name, rd->name);
- powercap_unregister_zone(rp->priv->control_type, &rd->power_zone);
- }
-
- return ret;
-}
-
-static int __init rapl_add_platform_domain(struct rapl_priv *priv)
-{
- struct rapl_domain *rd;
- struct powercap_zone *power_zone;
- struct reg_action ra;
- int ret;
-
- ra.reg = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
- ra.mask = ~0;
- ret = priv->read_raw(0, &ra);
- if (ret || !ra.value)
- return -ENODEV;
-
- ra.reg = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
- ra.mask = ~0;
- ret = priv->read_raw(0, &ra);
- if (ret || !ra.value)
- return -ENODEV;
-
- rd = kzalloc(sizeof(*rd), GFP_KERNEL);
- if (!rd)
- return -ENOMEM;
-
- rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
- rd->id = RAPL_DOMAIN_PLATFORM;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
- rd->regs[RAPL_DOMAIN_REG_STATUS] = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- rd->rpl[1].prim_id = PL2_ENABLE;
- rd->rpl[1].name = pl2_name;
- rd->rp = rapl_find_package_domain(0, priv);
-
- power_zone = powercap_register_zone(&rd->power_zone, priv->control_type,
- "psys", NULL,
- &zone_ops[RAPL_DOMAIN_PLATFORM],
- 2, &constraint_ops);
-
- if (IS_ERR(power_zone)) {
- kfree(rd);
- return PTR_ERR(power_zone);
- }
-
- priv->platform_rapl_domain = rd;
-
- return 0;
-}
-
-static void rapl_remove_platform_domain(struct rapl_priv *priv)
-{
- if (priv->platform_rapl_domain) {
- powercap_unregister_zone(priv->control_type,
- &priv->platform_rapl_domain->power_zone);
- kfree(priv->platform_rapl_domain);
- }
-}
-
-static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
-{
- struct reg_action ra;
-
- switch (domain) {
- case RAPL_DOMAIN_PACKAGE:
- case RAPL_DOMAIN_PP0:
- case RAPL_DOMAIN_PP1:
- case RAPL_DOMAIN_DRAM:
- ra.reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS];
- break;
- case RAPL_DOMAIN_PLATFORM:
- /* PSYS(PLATFORM) is not a CPU domain, so avoid printng error */
- return -EINVAL;
- default:
- pr_err("invalid domain id %d\n", domain);
- return -EINVAL;
- }
- /* make sure domain counters are available and contains non-zero
- * values, otherwise skip it.
- */
-
- ra.mask = ~0;
- if (rp->priv->read_raw(cpu, &ra) || !ra.value)
- return -ENODEV;
-
- return 0;
-}
-
-
-/*
- * Check if power limits are available. Two cases when they are not available:
- * 1. Locked by BIOS, in this case we still provide read-only access so that
- * users can see what limit is set by the BIOS.
- * 2. Some CPUs make some domains monitoring only which means PLx MSRs may not
- * exist at all. In this case, we do not show the contraints in powercap.
- *
- * Called after domains are detected and initialized.
- */
-static void rapl_detect_powerlimit(struct rapl_domain *rd)
-{
- u64 val64;
- int i;
-
- /* check if the domain is locked by BIOS, ignore if MSR doesn't exist */
- if (!rapl_read_data_raw(rd, FW_LOCK, false, &val64)) {
- if (val64) {
- pr_info("RAPL %s domain %s locked by BIOS\n",
- rd->rp->name, rd->name);
- rd->state |= DOMAIN_STATE_BIOS_LOCKED;
- }
- }
- /* check if power limit MSRs exists, otherwise domain is monitoring only */
- for (i = 0; i < NR_POWER_LIMITS; i++) {
- int prim = rd->rpl[i].prim_id;
- if (rapl_read_data_raw(rd, prim, false, &val64))
- rd->rpl[i].name = NULL;
- }
-}
-
-/* Detect active and valid domains for the given CPU, caller must
- * ensure the CPU belongs to the targeted package and CPU hotlug is disabled.
- */
-static int rapl_detect_domains(struct rapl_package *rp, int cpu)
-{
- struct rapl_domain *rd;
- int i;
-
- for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
- /* use physical package id to read counters */
- if (!rapl_check_domain(cpu, i, rp)) {
- rp->domain_map |= 1 << i;
- pr_info("Found RAPL domain %s\n", rapl_domain_names[i]);
- }
- }
- rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX);
- if (!rp->nr_domains) {
- pr_debug("no valid rapl domains found in %s\n", rp->name);
- return -ENODEV;
- }
- pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name);
-
- rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain),
- GFP_KERNEL);
- if (!rp->domains)
- return -ENOMEM;
-
- rapl_init_domains(rp);
-
- for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++)
- rapl_detect_powerlimit(rd);
-
- return 0;
-}
-
-/* called from CPU hotplug notifier, hotplug lock held */
-static void rapl_remove_package(struct rapl_package *rp)
-{
- struct rapl_domain *rd, *rd_package = NULL;
-
- package_power_limit_irq_restore(rp);
-
- for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
- rapl_write_data_raw(rd, PL1_ENABLE, 0);
- rapl_write_data_raw(rd, PL1_CLAMP, 0);
- if (find_nr_power_limit(rd) > 1) {
- rapl_write_data_raw(rd, PL2_ENABLE, 0);
- rapl_write_data_raw(rd, PL2_CLAMP, 0);
- }
- if (rd->id == RAPL_DOMAIN_PACKAGE) {
- rd_package = rd;
- continue;
- }
- pr_debug("remove package, undo power limit on %s: %s\n",
- rp->name, rd->name);
- powercap_unregister_zone(rp->priv->control_type, &rd->power_zone);
- }
- /* do parent zone last */
- powercap_unregister_zone(rp->priv->control_type, &rd_package->power_zone);
- list_del(&rp->plist);
- kfree(rp);
-}
-
-/* called from CPU hotplug notifier, hotplug lock held */
-static struct rapl_package *rapl_add_package(int cpu, struct rapl_priv *priv)
-{
- int id = topology_logical_die_id(cpu);
- struct rapl_package *rp;
- struct cpuinfo_x86 *c = &cpu_data(cpu);
- int ret;
-
- rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL);
- if (!rp)
- return ERR_PTR(-ENOMEM);
-
- /* add the new package to the list */
- rp->id = id;
- rp->lead_cpu = cpu;
- rp->priv = priv;
-
- if (topology_max_die_per_package() > 1)
- snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH,
- "package-%d-die-%d", c->phys_proc_id, c->cpu_die_id);
- else
- snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d",
- c->phys_proc_id);
-
- /* check if the package contains valid domains */
- if (rapl_detect_domains(rp, cpu) ||
- rapl_defaults->check_unit(rp, cpu)) {
- ret = -ENODEV;
- goto err_free_package;
- }
- ret = rapl_package_register_powercap(rp);
- if (!ret) {
- INIT_LIST_HEAD(&rp->plist);
- list_add(&rp->plist, &rapl_packages);
- return rp;
- }
-
-err_free_package:
- kfree(rp->domains);
- kfree(rp);
- return ERR_PTR(ret);
-}
-
/* Handles CPU hotplug on multi-socket systems.
* If a CPU goes online as the first CPU of the physical package
* we add the RAPL package to the system. Similarly, when the last
@@ -1387,92 +81,6 @@ static int rapl_cpu_down_prep(unsigned int cpu)
return 0;
}
-static void power_limit_state_save(void)
-{
- struct rapl_package *rp;
- struct rapl_domain *rd;
- int nr_pl, ret, i;
-
- get_online_cpus();
- list_for_each_entry(rp, &rapl_packages, plist) {
- if (!rp->power_zone)
- continue;
- rd = power_zone_to_rapl_domain(rp->power_zone);
- nr_pl = find_nr_power_limit(rd);
- for (i = 0; i < nr_pl; i++) {
- switch (rd->rpl[i].prim_id) {
- case PL1_ENABLE:
- ret = rapl_read_data_raw(rd,
- POWER_LIMIT1,
- true,
- &rd->rpl[i].last_power_limit);
- if (ret)
- rd->rpl[i].last_power_limit = 0;
- break;
- case PL2_ENABLE:
- ret = rapl_read_data_raw(rd,
- POWER_LIMIT2,
- true,
- &rd->rpl[i].last_power_limit);
- if (ret)
- rd->rpl[i].last_power_limit = 0;
- break;
- }
- }
- }
- put_online_cpus();
-}
-
-static void power_limit_state_restore(void)
-{
- struct rapl_package *rp;
- struct rapl_domain *rd;
- int nr_pl, i;
-
- get_online_cpus();
- list_for_each_entry(rp, &rapl_packages, plist) {
- if (!rp->power_zone)
- continue;
- rd = power_zone_to_rapl_domain(rp->power_zone);
- nr_pl = find_nr_power_limit(rd);
- for (i = 0; i < nr_pl; i++) {
- switch (rd->rpl[i].prim_id) {
- case PL1_ENABLE:
- if (rd->rpl[i].last_power_limit)
- rapl_write_data_raw(rd,
- POWER_LIMIT1,
- rd->rpl[i].last_power_limit);
- break;
- case PL2_ENABLE:
- if (rd->rpl[i].last_power_limit)
- rapl_write_data_raw(rd,
- POWER_LIMIT2,
- rd->rpl[i].last_power_limit);
- break;
- }
- }
- }
- put_online_cpus();
-}
-
-static int rapl_pm_callback(struct notifier_block *nb,
- unsigned long mode, void *_unused)
-{
- switch (mode) {
- case PM_SUSPEND_PREPARE:
- power_limit_state_save();
- break;
- case PM_POST_SUSPEND:
- power_limit_state_restore();
- break;
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block rapl_pm_notifier = {
- .notifier_call = rapl_pm_callback,
-};
-
static int rapl_msr_read_raw(int cpu, struct reg_action *ra)
{
if (rdmsrl_safe_on_cpu(cpu, ra->reg, &ra->value)) {
@@ -1498,7 +106,6 @@ static void rapl_msr_update_func(void *info)
ra->err = wrmsrl_safe(ra->reg, val);
}
-
static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
{
int ret;
@@ -1511,21 +118,10 @@ static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
return ret;
}
-static int __init rapl_init(void)
+static int __init rapl_msr_init(void)
{
- const struct x86_cpu_id *id;
int ret;
- id = x86_match_cpu(rapl_ids);
- if (!id) {
- pr_err("driver does not support CPU family %d model %d\n",
- boot_cpu_data.x86, boot_cpu_data.x86_model);
-
- return -ENODEV;
- }
-
- rapl_defaults = (struct rapl_defaults *)id->driver_data;
-
rapl_msr_priv.read_raw = rapl_msr_read_raw;
rapl_msr_priv.write_raw = rapl_msr_write_raw;
@@ -1538,36 +134,29 @@ static int __init rapl_init(void)
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
rapl_cpu_online, rapl_cpu_down_prep);
if (ret < 0)
- goto err_unreg;
+ goto out;
rapl_msr_priv.pcap_rapl_online = ret;
/* Don't bail out if PSys is not supported */
rapl_add_platform_domain(&rapl_msr_priv);
- ret = register_pm_notifier(&rapl_pm_notifier);
- if (ret)
- goto err_unreg_all;
-
return 0;
-err_unreg_all:
- cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
-
-err_unreg:
- powercap_unregister_control_type(rapl_msr_priv.control_type);
+out:
+ if (ret)
+ powercap_unregister_control_type(rapl_msr_priv.control_type);
return ret;
}
-static void __exit rapl_exit(void)
+static void __exit rapl_msr_exit(void)
{
- unregister_pm_notifier(&rapl_pm_notifier);
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
rapl_remove_platform_domain(&rapl_msr_priv);
powercap_unregister_control_type(rapl_msr_priv.control_type);
}
-module_init(rapl_init);
-module_exit(rapl_exit);
+module_init(rapl_msr_init);
+module_exit(rapl_msr_exit);
MODULE_DESCRIPTION("Driver for Intel RAPL (Running Average Power Limit)");
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>");
diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c
new file mode 100644
index 0000000..8f589cf
--- /dev/null
+++ b/drivers/powercap/intel_rapl_common.c
@@ -0,0 +1,1468 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Common code for Intel Running Average Power Limit (RAPL) support.
+ * Copyright (c) 2019, Intel Corporation.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+#include <linux/bitmap.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/cpu.h>
+#include <linux/powercap.h>
+#include <linux/suspend.h>
+#include <asm/iosf_mbi.h>
+#include <linux/intel_rapl.h>
+
+#include <linux/processor.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+/* Local defines */
+#define MSR_PLATFORM_POWER_LIMIT 0x0000065C
+
+/* bitmasks for RAPL MSRs, used by primitive access functions */
+#define ENERGY_STATUS_MASK 0xffffffff
+
+#define POWER_LIMIT1_MASK 0x7FFF
+#define POWER_LIMIT1_ENABLE BIT(15)
+#define POWER_LIMIT1_CLAMP BIT(16)
+
+#define POWER_LIMIT2_MASK (0x7FFFULL<<32)
+#define POWER_LIMIT2_ENABLE BIT_ULL(47)
+#define POWER_LIMIT2_CLAMP BIT_ULL(48)
+#define POWER_PACKAGE_LOCK BIT_ULL(63)
+#define POWER_PP_LOCK BIT(31)
+
+#define TIME_WINDOW1_MASK (0x7FULL<<17)
+#define TIME_WINDOW2_MASK (0x7FULL<<49)
+
+#define POWER_UNIT_OFFSET 0
+#define POWER_UNIT_MASK 0x0F
+
+#define ENERGY_UNIT_OFFSET 0x08
+#define ENERGY_UNIT_MASK 0x1F00
+
+#define TIME_UNIT_OFFSET 0x10
+#define TIME_UNIT_MASK 0xF0000
+
+#define POWER_INFO_MAX_MASK (0x7fffULL<<32)
+#define POWER_INFO_MIN_MASK (0x7fffULL<<16)
+#define POWER_INFO_MAX_TIME_WIN_MASK (0x3fULL<<48)
+#define POWER_INFO_THERMAL_SPEC_MASK 0x7fff
+
+#define PERF_STATUS_THROTTLE_TIME_MASK 0xffffffff
+#define PP_POLICY_MASK 0x1F
+
+/* Non HW constants */
+#define RAPL_PRIMITIVE_DERIVED BIT(1) /* not from raw data */
+#define RAPL_PRIMITIVE_DUMMY BIT(2)
+
+#define TIME_WINDOW_MAX_MSEC 40000
+#define TIME_WINDOW_MIN_MSEC 250
+#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */
+enum unit_type {
+ ARBITRARY_UNIT, /* no translation */
+ POWER_UNIT,
+ ENERGY_UNIT,
+ TIME_UNIT,
+};
+
+/* per domain data, some are optional */
+#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2)
+
+#define DOMAIN_STATE_INACTIVE BIT(0)
+#define DOMAIN_STATE_POWER_LIMIT_SET BIT(1)
+#define DOMAIN_STATE_BIOS_LOCKED BIT(2)
+
+static const char pl1_name[] = "long_term";
+static const char pl2_name[] = "short_term";
+
+#define power_zone_to_rapl_domain(_zone) \
+ container_of(_zone, struct rapl_domain, power_zone)
+
+struct rapl_defaults {
+ u8 floor_freq_reg_addr;
+ int (*check_unit)(struct rapl_package *rp, int cpu);
+ void (*set_floor_freq)(struct rapl_domain *rd, bool mode);
+ u64 (*compute_time_window)(struct rapl_package *rp, u64 val,
+ bool to_raw);
+ unsigned int dram_domain_energy_unit;
+};
+static struct rapl_defaults *rapl_defaults;
+
+/* Sideband MBI registers */
+#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2)
+#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf)
+
+#define PACKAGE_PLN_INT_SAVED BIT(0)
+#define MAX_PRIM_NAME (32)
+
+/* per domain data. used to describe individual knobs such that access function
+ * can be consolidated into one instead of many inline functions.
+ */
+struct rapl_primitive_info {
+ const char *name;
+ u64 mask;
+ int shift;
+ enum rapl_domain_reg_id id;
+ enum unit_type unit;
+ u32 flag;
+};
+
+#define PRIMITIVE_INFO_INIT(p, m, s, i, u, f) { \
+ .name = #p, \
+ .mask = m, \
+ .shift = s, \
+ .id = i, \
+ .unit = u, \
+ .flag = f \
+ }
+
+static void rapl_init_domains(struct rapl_package *rp);
+static int rapl_read_data_raw(struct rapl_domain *rd,
+ enum rapl_primitives prim,
+ bool xlate, u64 *data);
+static int rapl_write_data_raw(struct rapl_domain *rd,
+ enum rapl_primitives prim,
+ unsigned long long value);
+static u64 rapl_unit_xlate(struct rapl_domain *rd,
+ enum unit_type type, u64 value, int to_raw);
+static void package_power_limit_irq_save(struct rapl_package *rp);
+static int rapl_init_core(void);
+static void rapl_remove_core(void);
+
+static LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */
+
+static const char *const rapl_domain_names[] = {
+ "package",
+ "core",
+ "uncore",
+ "dram",
+ "psys",
+};
+
+static int get_energy_counter(struct powercap_zone *power_zone,
+ u64 *energy_raw)
+{
+ struct rapl_domain *rd;
+ u64 energy_now;
+
+ /* prevent CPU hotplug, make sure the RAPL domain does not go
+ * away while reading the counter.
+ */
+ get_online_cpus();
+ rd = power_zone_to_rapl_domain(power_zone);
+
+ if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now)) {
+ *energy_raw = energy_now;
+ put_online_cpus();
+
+ return 0;
+ }
+ put_online_cpus();
+
+ return -EIO;
+}
+
+static int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy)
+{
+ struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev);
+
+ *energy = rapl_unit_xlate(rd, ENERGY_UNIT, ENERGY_STATUS_MASK, 0);
+ return 0;
+}
+
+static int release_zone(struct powercap_zone *power_zone)
+{
+ struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone);
+ struct rapl_package *rp = rd->rp;
+
+ /* package zone is the last zone of a package, we can free
+ * memory here since all children has been unregistered.
+ */
+ if (rd->id == RAPL_DOMAIN_PACKAGE) {
+ kfree(rd);
+ rp->domains = NULL;
+ }
+
+ return 0;
+
+}
+
+static int find_nr_power_limit(struct rapl_domain *rd)
+{
+ int i, nr_pl = 0;
+
+ for (i = 0; i < NR_POWER_LIMITS; i++) {
+ if (rd->rpl[i].name)
+ nr_pl++;
+ }
+
+ return nr_pl;
+}
+
+static int set_domain_enable(struct powercap_zone *power_zone, bool mode)
+{
+ struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone);
+
+ if (rd->state & DOMAIN_STATE_BIOS_LOCKED)
+ return -EACCES;
+
+ get_online_cpus();
+ rapl_write_data_raw(rd, PL1_ENABLE, mode);
+ if (rapl_defaults->set_floor_freq)
+ rapl_defaults->set_floor_freq(rd, mode);
+ put_online_cpus();
+
+ return 0;
+}
+
+static int get_domain_enable(struct powercap_zone *power_zone, bool *mode)
+{
+ struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone);
+ u64 val;
+
+ if (rd->state & DOMAIN_STATE_BIOS_LOCKED) {
+ *mode = false;
+ return 0;
+ }
+ get_online_cpus();
+ if (rapl_read_data_raw(rd, PL1_ENABLE, true, &val)) {
+ put_online_cpus();
+ return -EIO;
+ }
+ *mode = val;
+ put_online_cpus();
+
+ return 0;
+}
+
+/* per RAPL domain ops, in the order of rapl_domain_type */
+static const struct powercap_zone_ops zone_ops[] = {
+ /* RAPL_DOMAIN_PACKAGE */
+ {
+ .get_energy_uj = get_energy_counter,
+ .get_max_energy_range_uj = get_max_energy_counter,
+ .release = release_zone,
+ .set_enable = set_domain_enable,
+ .get_enable = get_domain_enable,
+ },
+ /* RAPL_DOMAIN_PP0 */
+ {
+ .get_energy_uj = get_energy_counter,
+ .get_max_energy_range_uj = get_max_energy_counter,
+ .release = release_zone,
+ .set_enable = set_domain_enable,
+ .get_enable = get_domain_enable,
+ },
+ /* RAPL_DOMAIN_PP1 */
+ {
+ .get_energy_uj = get_energy_counter,
+ .get_max_energy_range_uj = get_max_energy_counter,
+ .release = release_zone,
+ .set_enable = set_domain_enable,
+ .get_enable = get_domain_enable,
+ },
+ /* RAPL_DOMAIN_DRAM */
+ {
+ .get_energy_uj = get_energy_counter,
+ .get_max_energy_range_uj = get_max_energy_counter,
+ .release = release_zone,
+ .set_enable = set_domain_enable,
+ .get_enable = get_domain_enable,
+ },
+ /* RAPL_DOMAIN_PLATFORM */
+ {
+ .get_energy_uj = get_energy_counter,
+ .get_max_energy_range_uj = get_max_energy_counter,
+ .release = release_zone,
+ .set_enable = set_domain_enable,
+ .get_enable = get_domain_enable,
+ },
+};
+
+/*
+ * Constraint index used by powercap can be different than power limit (PL)
+ * index in that some PLs maybe missing due to non-existent MSRs. So we
+ * need to convert here by finding the valid PLs only (name populated).
+ */
+static int contraint_to_pl(struct rapl_domain *rd, int cid)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < NR_POWER_LIMITS; i++) {
+ if ((rd->rpl[i].name) && j++ == cid) {
+ pr_debug("%s: index %d\n", __func__, i);
+ return i;
+ }
+ }
+ pr_err("Cannot find matching power limit for constraint %d\n", cid);
+
+ return -EINVAL;
+}
+
+static int set_power_limit(struct powercap_zone *power_zone, int cid,
+ u64 power_limit)
+{
+ struct rapl_domain *rd;
+ struct rapl_package *rp;
+ int ret = 0;
+ int id;
+
+ get_online_cpus();
+ rd = power_zone_to_rapl_domain(power_zone);
+ id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto set_exit;
+ }
+
+ rp = rd->rp;
+
+ if (rd->state & DOMAIN_STATE_BIOS_LOCKED) {
+ dev_warn(&power_zone->dev,
+ "%s locked by BIOS, monitoring only\n", rd->name);
+ ret = -EACCES;
+ goto set_exit;
+ }
+
+ switch (rd->rpl[id].prim_id) {
+ case PL1_ENABLE:
+ rapl_write_data_raw(rd, POWER_LIMIT1, power_limit);
+ break;
+ case PL2_ENABLE:
+ rapl_write_data_raw(rd, POWER_LIMIT2, power_limit);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (!ret)
+ package_power_limit_irq_save(rp);
+set_exit:
+ put_online_cpus();
+ return ret;
+}
+
+static int get_current_power_limit(struct powercap_zone *power_zone, int cid,
+ u64 *data)
+{
+ struct rapl_domain *rd;
+ u64 val;
+ int prim;
+ int ret = 0;
+ int id;
+
+ get_online_cpus();
+ rd = power_zone_to_rapl_domain(power_zone);
+ id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto get_exit;
+ }
+
+ switch (rd->rpl[id].prim_id) {
+ case PL1_ENABLE:
+ prim = POWER_LIMIT1;
+ break;
+ case PL2_ENABLE:
+ prim = POWER_LIMIT2;
+ break;
+ default:
+ put_online_cpus();
+ return -EINVAL;
+ }
+ if (rapl_read_data_raw(rd, prim, true, &val))
+ ret = -EIO;
+ else
+ *data = val;
+
+get_exit:
+ put_online_cpus();
+
+ return ret;
+}
+
+static int set_time_window(struct powercap_zone *power_zone, int cid,
+ u64 window)
+{
+ struct rapl_domain *rd;
+ int ret = 0;
+ int id;
+
+ get_online_cpus();
+ rd = power_zone_to_rapl_domain(power_zone);
+ id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto set_time_exit;
+ }
+
+ switch (rd->rpl[id].prim_id) {
+ case PL1_ENABLE:
+ rapl_write_data_raw(rd, TIME_WINDOW1, window);
+ break;
+ case PL2_ENABLE:
+ rapl_write_data_raw(rd, TIME_WINDOW2, window);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+set_time_exit:
+ put_online_cpus();
+ return ret;
+}
+
+static int get_time_window(struct powercap_zone *power_zone, int cid,
+ u64 *data)
+{
+ struct rapl_domain *rd;
+ u64 val;
+ int ret = 0;
+ int id;
+
+ get_online_cpus();
+ rd = power_zone_to_rapl_domain(power_zone);
+ id = contraint_to_pl(rd, cid);
+ if (id < 0) {
+ ret = id;
+ goto get_time_exit;
+ }
+
+ switch (rd->rpl[id].prim_id) {
+ case PL1_ENABLE:
+ ret = rapl_read_data_raw(rd, TIME_WINDOW1, true, &val);
+ break;
+ case PL2_ENABLE:
+ ret = rapl_read_data_raw(rd, TIME_WINDOW2, true, &val);
+ break;
+ default:
+ put_online_cpus();
+ return -EINVAL;
+ }
+ if (!ret)
+ *data = val;
+
+get_time_exit:
+ put_online_cpus();
+
+ return ret;
+}
+
+static const char *get_constraint_name(struct powercap_zone *power_zone,
+ int cid)
+{
+ struct rapl_domain *rd;
+ int id;
+
+ rd = power_zone_to_rapl_domain(power_zone);
+ id = contraint_to_pl(rd, cid);
+ if (id >= 0)
+ return rd->rpl[id].name;
+
+ return NULL;
+}
+
+static int get_max_power(struct powercap_zone *power_zone, int id, u64 *data)
+{
+ struct rapl_domain *rd;
+ u64 val;
+ int prim;
+ int ret = 0;
+
+ get_online_cpus();
+ rd = power_zone_to_rapl_domain(power_zone);
+ switch (rd->rpl[id].prim_id) {
+ case PL1_ENABLE:
+ prim = THERMAL_SPEC_POWER;
+ break;
+ case PL2_ENABLE:
+ prim = MAX_POWER;
+ break;
+ default:
+ put_online_cpus();
+ return -EINVAL;
+ }
+ if (rapl_read_data_raw(rd, prim, true, &val))
+ ret = -EIO;
+ else
+ *data = val;
+
+ put_online_cpus();
+
+ return ret;
+}
+
+static const struct powercap_zone_constraint_ops constraint_ops = {
+ .set_power_limit_uw = set_power_limit,
+ .get_power_limit_uw = get_current_power_limit,
+ .set_time_window_us = set_time_window,
+ .get_time_window_us = get_time_window,
+ .get_max_power_uw = get_max_power,
+ .get_name = get_constraint_name,
+};
+
+/* called after domain detection and package level data are set */
+static void rapl_init_domains(struct rapl_package *rp)
+{
+ int i;
+ struct rapl_domain *rd = rp->domains;
+
+ for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
+ unsigned int mask = rp->domain_map & (1 << i);
+
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] =
+ rp->priv->regs[i][RAPL_DOMAIN_REG_LIMIT];
+ rd->regs[RAPL_DOMAIN_REG_STATUS] =
+ rp->priv->regs[i][RAPL_DOMAIN_REG_STATUS];
+ rd->regs[RAPL_DOMAIN_REG_PERF] =
+ rp->priv->regs[i][RAPL_DOMAIN_REG_PERF];
+ rd->regs[RAPL_DOMAIN_REG_POLICY] =
+ rp->priv->regs[i][RAPL_DOMAIN_REG_POLICY];
+ rd->regs[RAPL_DOMAIN_REG_INFO] =
+ rp->priv->regs[i][RAPL_DOMAIN_REG_INFO];
+
+ switch (mask) {
+ case BIT(RAPL_DOMAIN_PACKAGE):
+ rd->name = rapl_domain_names[RAPL_DOMAIN_PACKAGE];
+ rd->id = RAPL_DOMAIN_PACKAGE;
+ rd->rpl[0].prim_id = PL1_ENABLE;
+ rd->rpl[0].name = pl1_name;
+ rd->rpl[1].prim_id = PL2_ENABLE;
+ rd->rpl[1].name = pl2_name;
+ break;
+ case BIT(RAPL_DOMAIN_PP0):
+ rd->name = rapl_domain_names[RAPL_DOMAIN_PP0];
+ rd->id = RAPL_DOMAIN_PP0;
+ rd->rpl[0].prim_id = PL1_ENABLE;
+ rd->rpl[0].name = pl1_name;
+ break;
+ case BIT(RAPL_DOMAIN_PP1):
+ rd->name = rapl_domain_names[RAPL_DOMAIN_PP1];
+ rd->id = RAPL_DOMAIN_PP1;
+ rd->rpl[0].prim_id = PL1_ENABLE;
+ rd->rpl[0].name = pl1_name;
+ break;
+ case BIT(RAPL_DOMAIN_DRAM):
+ rd->name = rapl_domain_names[RAPL_DOMAIN_DRAM];
+ rd->id = RAPL_DOMAIN_DRAM;
+ rd->rpl[0].prim_id = PL1_ENABLE;
+ rd->rpl[0].name = pl1_name;
+ rd->domain_energy_unit =
+ rapl_defaults->dram_domain_energy_unit;
+ if (rd->domain_energy_unit)
+ pr_info("DRAM domain energy unit %dpj\n",
+ rd->domain_energy_unit);
+ break;
+ }
+ if (mask) {
+ rd->rp = rp;
+ rd++;
+ }
+ }
+}
+
+static u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type,
+ u64 value, int to_raw)
+{
+ u64 units = 1;
+ struct rapl_package *rp = rd->rp;
+ u64 scale = 1;
+
+ switch (type) {
+ case POWER_UNIT:
+ units = rp->power_unit;
+ break;
+ case ENERGY_UNIT:
+ scale = ENERGY_UNIT_SCALE;
+ /* per domain unit takes precedence */
+ if (rd->domain_energy_unit)
+ units = rd->domain_energy_unit;
+ else
+ units = rp->energy_unit;
+ break;
+ case TIME_UNIT:
+ return rapl_defaults->compute_time_window(rp, value, to_raw);
+ case ARBITRARY_UNIT:
+ default:
+ return value;
+ };
+
+ if (to_raw)
+ return div64_u64(value, units) * scale;
+
+ value *= units;
+
+ return div64_u64(value, scale);
+}
+
+/* in the order of enum rapl_primitives */
+static struct rapl_primitive_info rpi[] = {
+ /* name, mask, shift, msr index, unit divisor */
+ PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0,
+ RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0),
+ PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0,
+ RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
+ PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32,
+ RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
+ PRIMITIVE_INFO_INIT(FW_LOCK, POWER_PP_LOCK, 31,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48,
+ RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
+ PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17,
+ RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
+ PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49,
+ RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0),
+ PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK,
+ 0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
+ PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32,
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
+ PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16,
+ RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0),
+ PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48,
+ RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0),
+ PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0,
+ RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0),
+ PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0,
+ RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0),
+ /* non-hardware */
+ PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT,
+ RAPL_PRIMITIVE_DERIVED),
+ {NULL, 0, 0, 0},
+};
+
+/* Read primitive data based on its related struct rapl_primitive_info.
+ * if xlate flag is set, return translated data based on data units, i.e.
+ * time, energy, and power.
+ * RAPL MSRs are non-architectual and are laid out not consistently across
+ * domains. Here we use primitive info to allow writing consolidated access
+ * functions.
+ * For a given primitive, it is processed by MSR mask and shift. Unit conversion
+ * is pre-assigned based on RAPL unit MSRs read at init time.
+ * 63-------------------------- 31--------------------------- 0
+ * | xxxxx (mask) |
+ * | |<- shift ----------------|
+ * 63-------------------------- 31--------------------------- 0
+ */
+static int rapl_read_data_raw(struct rapl_domain *rd,
+ enum rapl_primitives prim, bool xlate, u64 *data)
+{
+ u64 value;
+ struct rapl_primitive_info *rp = &rpi[prim];
+ struct reg_action ra;
+ int cpu;
+
+ if (!rp->name || rp->flag & RAPL_PRIMITIVE_DUMMY)
+ return -EINVAL;
+
+ ra.reg = rd->regs[rp->id];
+ if (!ra.reg)
+ return -EINVAL;
+
+ cpu = rd->rp->lead_cpu;
+
+ /* special-case package domain, which uses a different bit */
+ if (prim == FW_LOCK && rd->id == RAPL_DOMAIN_PACKAGE) {
+ rp->mask = POWER_PACKAGE_LOCK;
+ rp->shift = 63;
+ }
+ /* non-hardware data are collected by the polling thread */
+ if (rp->flag & RAPL_PRIMITIVE_DERIVED) {
+ *data = rd->rdd.primitives[prim];
+ return 0;
+ }
+
+ ra.mask = rp->mask;
+
+ if (rd->rp->priv->read_raw(cpu, &ra)) {
+ pr_debug("failed to read reg 0x%x on cpu %d\n", ra.reg, cpu);
+ return -EIO;
+ }
+
+ value = ra.value >> rp->shift;
+
+ if (xlate)
+ *data = rapl_unit_xlate(rd, rp->unit, value, 0);
+ else
+ *data = value;
+
+ return 0;
+}
+
+/* Similar use of primitive info in the read counterpart */
+static int rapl_write_data_raw(struct rapl_domain *rd,
+ enum rapl_primitives prim,
+ unsigned long long value)
+{
+ struct rapl_primitive_info *rp = &rpi[prim];
+ int cpu;
+ u64 bits;
+ struct reg_action ra;
+ int ret;
+
+ cpu = rd->rp->lead_cpu;
+ bits = rapl_unit_xlate(rd, rp->unit, value, 1);
+ bits <<= rp->shift;
+ bits &= rp->mask;
+
+ memset(&ra, 0, sizeof(ra));
+
+ ra.reg = rd->regs[rp->id];
+ ra.mask = rp->mask;
+ ra.value = bits;
+
+ ret = rd->rp->priv->write_raw(cpu, &ra);
+
+ return ret;
+}
+
+/*
+ * Raw RAPL data stored in MSRs are in certain scales. We need to
+ * convert them into standard units based on the units reported in
+ * the RAPL unit MSRs. This is specific to CPUs as the method to
+ * calculate units differ on different CPUs.
+ * We convert the units to below format based on CPUs.
+ * i.e.
+ * energy unit: picoJoules : Represented in picoJoules by default
+ * power unit : microWatts : Represented in milliWatts by default
+ * time unit : microseconds: Represented in seconds by default
+ */
+static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
+{
+ struct reg_action ra;
+ u32 value;
+
+ ra.reg = rp->priv->reg_unit;
+ ra.mask = ~0;
+ if (rp->priv->read_raw(cpu, &ra)) {
+ pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
+ rp->priv->reg_unit, cpu);
+ return -ENODEV;
+ }
+
+ value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
+ rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value);
+
+ value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
+ rp->power_unit = 1000000 / (1 << value);
+
+ value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
+ rp->time_unit = 1000000 / (1 << value);
+
+ pr_debug("Core CPU %s energy=%dpJ, time=%dus, power=%duW\n",
+ rp->name, rp->energy_unit, rp->time_unit, rp->power_unit);
+
+ return 0;
+}
+
+static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
+{
+ struct reg_action ra;
+ u32 value;
+
+ ra.reg = rp->priv->reg_unit;
+ ra.mask = ~0;
+ if (rp->priv->read_raw(cpu, &ra)) {
+ pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
+ rp->priv->reg_unit, cpu);
+ return -ENODEV;
+ }
+
+ value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
+ rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value;
+
+ value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
+ rp->power_unit = (1 << value) * 1000;
+
+ value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
+ rp->time_unit = 1000000 / (1 << value);
+
+ pr_debug("Atom %s energy=%dpJ, time=%dus, power=%duW\n",
+ rp->name, rp->energy_unit, rp->time_unit, rp->power_unit);
+
+ return 0;
+}
+
+static void power_limit_irq_save_cpu(void *info)
+{
+ u32 l, h = 0;
+ struct rapl_package *rp = (struct rapl_package *)info;
+
+ /* save the state of PLN irq mask bit before disabling it */
+ rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h);
+ if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED)) {
+ rp->power_limit_irq = l & PACKAGE_THERM_INT_PLN_ENABLE;
+ rp->power_limit_irq |= PACKAGE_PLN_INT_SAVED;
+ }
+ l &= ~PACKAGE_THERM_INT_PLN_ENABLE;
+ wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
+}
+
+/* REVISIT:
+ * When package power limit is set artificially low by RAPL, LVT
+ * thermal interrupt for package power limit should be ignored
+ * since we are not really exceeding the real limit. The intention
+ * is to avoid excessive interrupts while we are trying to save power.
+ * A useful feature might be routing the package_power_limit interrupt
+ * to userspace via eventfd. once we have a usecase, this is simple
+ * to do by adding an atomic notifier.
+ */
+
+static void package_power_limit_irq_save(struct rapl_package *rp)
+{
+ if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN))
+ return;
+
+ smp_call_function_single(rp->lead_cpu, power_limit_irq_save_cpu, rp, 1);
+}
+
+/*
+ * Restore per package power limit interrupt enable state. Called from cpu
+ * hotplug code on package removal.
+ */
+static void package_power_limit_irq_restore(struct rapl_package *rp)
+{
+ u32 l, h;
+
+ if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN))
+ return;
+
+ /* irq enable state not saved, nothing to restore */
+ if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED))
+ return;
+
+ rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h);
+
+ if (rp->power_limit_irq & PACKAGE_THERM_INT_PLN_ENABLE)
+ l |= PACKAGE_THERM_INT_PLN_ENABLE;
+ else
+ l &= ~PACKAGE_THERM_INT_PLN_ENABLE;
+
+ wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
+}
+
+static void set_floor_freq_default(struct rapl_domain *rd, bool mode)
+{
+ int nr_powerlimit = find_nr_power_limit(rd);
+
+ /* always enable clamp such that p-state can go below OS requested
+ * range. power capping priority over guranteed frequency.
+ */
+ rapl_write_data_raw(rd, PL1_CLAMP, mode);
+
+ /* some domains have pl2 */
+ if (nr_powerlimit > 1) {
+ rapl_write_data_raw(rd, PL2_ENABLE, mode);
+ rapl_write_data_raw(rd, PL2_CLAMP, mode);
+ }
+}
+
+static void set_floor_freq_atom(struct rapl_domain *rd, bool enable)
+{
+ static u32 power_ctrl_orig_val;
+ u32 mdata;
+
+ if (!rapl_defaults->floor_freq_reg_addr) {
+ pr_err("Invalid floor frequency config register\n");
+ return;
+ }
+
+ if (!power_ctrl_orig_val)
+ iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_CR_READ,
+ rapl_defaults->floor_freq_reg_addr,
+ &power_ctrl_orig_val);
+ mdata = power_ctrl_orig_val;
+ if (enable) {
+ mdata &= ~(0x7f << 8);
+ mdata |= 1 << 8;
+ }
+ iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_CR_WRITE,
+ rapl_defaults->floor_freq_reg_addr, mdata);
+}
+
+static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
+ bool to_raw)
+{
+ u64 f, y; /* fraction and exp. used for time unit */
+
+ /*
+ * Special processing based on 2^Y*(1+F/4), refer
+ * to Intel Software Developer's manual Vol.3B: CH 14.9.3.
+ */
+ if (!to_raw) {
+ f = (value & 0x60) >> 5;
+ y = value & 0x1f;
+ value = (1 << y) * (4 + f) * rp->time_unit / 4;
+ } else {
+ do_div(value, rp->time_unit);
+ y = ilog2(value);
+ f = div64_u64(4 * (value - (1 << y)), 1 << y);
+ value = (y & 0x1f) | ((f & 0x3) << 5);
+ }
+ return value;
+}
+
+static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
+ bool to_raw)
+{
+ /*
+ * Atom time unit encoding is straight forward val * time_unit,
+ * where time_unit is default to 1 sec. Never 0.
+ */
+ if (!to_raw)
+ return (value) ? value *= rp->time_unit : rp->time_unit;
+
+ value = div64_u64(value, rp->time_unit);
+
+ return value;
+}
+
+static const struct rapl_defaults rapl_defaults_core = {
+ .floor_freq_reg_addr = 0,
+ .check_unit = rapl_check_unit_core,
+ .set_floor_freq = set_floor_freq_default,
+ .compute_time_window = rapl_compute_time_window_core,
+};
+
+static const struct rapl_defaults rapl_defaults_hsw_server = {
+ .check_unit = rapl_check_unit_core,
+ .set_floor_freq = set_floor_freq_default,
+ .compute_time_window = rapl_compute_time_window_core,
+ .dram_domain_energy_unit = 15300,
+};
+
+static const struct rapl_defaults rapl_defaults_byt = {
+ .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = set_floor_freq_atom,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_tng = {
+ .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = set_floor_freq_atom,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_ann = {
+ .floor_freq_reg_addr = 0,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = NULL,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_cht = {
+ .floor_freq_reg_addr = 0,
+ .check_unit = rapl_check_unit_atom,
+ .set_floor_freq = NULL,
+ .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct x86_cpu_id rapl_ids[] = {
+ INTEL_CPU_FAM6(SANDYBRIDGE, rapl_defaults_core),
+ INTEL_CPU_FAM6(SANDYBRIDGE_X, rapl_defaults_core),
+
+ INTEL_CPU_FAM6(IVYBRIDGE, rapl_defaults_core),
+ INTEL_CPU_FAM6(IVYBRIDGE_X, rapl_defaults_core),
+
+ INTEL_CPU_FAM6(HASWELL_CORE, rapl_defaults_core),
+ INTEL_CPU_FAM6(HASWELL_ULT, rapl_defaults_core),
+ INTEL_CPU_FAM6(HASWELL_GT3E, rapl_defaults_core),
+ INTEL_CPU_FAM6(HASWELL_X, rapl_defaults_hsw_server),
+
+ INTEL_CPU_FAM6(BROADWELL_CORE, rapl_defaults_core),
+ INTEL_CPU_FAM6(BROADWELL_GT3E, rapl_defaults_core),
+ INTEL_CPU_FAM6(BROADWELL_XEON_D, rapl_defaults_core),
+ INTEL_CPU_FAM6(BROADWELL_X, rapl_defaults_hsw_server),
+
+ INTEL_CPU_FAM6(SKYLAKE_DESKTOP, rapl_defaults_core),
+ INTEL_CPU_FAM6(SKYLAKE_MOBILE, rapl_defaults_core),
+ INTEL_CPU_FAM6(SKYLAKE_X, rapl_defaults_hsw_server),
+ INTEL_CPU_FAM6(KABYLAKE_MOBILE, rapl_defaults_core),
+ INTEL_CPU_FAM6(KABYLAKE_DESKTOP, rapl_defaults_core),
+ INTEL_CPU_FAM6(CANNONLAKE_MOBILE, rapl_defaults_core),
+ INTEL_CPU_FAM6(ICELAKE_MOBILE, rapl_defaults_core),
+
+ INTEL_CPU_FAM6(ATOM_SILVERMONT, rapl_defaults_byt),
+ INTEL_CPU_FAM6(ATOM_AIRMONT, rapl_defaults_cht),
+ INTEL_CPU_FAM6(ATOM_SILVERMONT_MID, rapl_defaults_tng),
+ INTEL_CPU_FAM6(ATOM_AIRMONT_MID, rapl_defaults_ann),
+ INTEL_CPU_FAM6(ATOM_GOLDMONT, rapl_defaults_core),
+ INTEL_CPU_FAM6(ATOM_GOLDMONT_PLUS, rapl_defaults_core),
+ INTEL_CPU_FAM6(ATOM_GOLDMONT_X, rapl_defaults_core),
+ INTEL_CPU_FAM6(ATOM_TREMONT_X, rapl_defaults_core),
+
+ INTEL_CPU_FAM6(XEON_PHI_KNL, rapl_defaults_hsw_server),
+ INTEL_CPU_FAM6(XEON_PHI_KNM, rapl_defaults_hsw_server),
+ {}
+};
+
+MODULE_DEVICE_TABLE(x86cpu, rapl_ids);
+
+/* Read once for all raw primitive data for domains */
+static void rapl_update_domain_data(struct rapl_package *rp)
+{
+ int dmn, prim;
+ u64 val;
+
+ for (dmn = 0; dmn < rp->nr_domains; dmn++) {
+ pr_debug("update %s domain %s data\n", rp->name,
+ rp->domains[dmn].name);
+ /* exclude non-raw primitives */
+ for (prim = 0; prim < NR_RAW_PRIMITIVES; prim++) {
+ if (!rapl_read_data_raw(&rp->domains[dmn], prim,
+ rpi[prim].unit, &val))
+ rp->domains[dmn].rdd.primitives[prim] = val;
+ }
+ }
+
+}
+
+static int rapl_package_register_powercap(struct rapl_package *rp)
+{
+ struct rapl_domain *rd;
+ struct powercap_zone *power_zone = NULL;
+ int nr_pl, ret;
+
+ /* Update the domain data of the new package */
+ rapl_update_domain_data(rp);
+
+ /* first we register package domain as the parent zone */
+ for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
+ if (rd->id == RAPL_DOMAIN_PACKAGE) {
+ nr_pl = find_nr_power_limit(rd);
+ pr_debug("register package domain %s\n", rp->name);
+ power_zone = powercap_register_zone(&rd->power_zone,
+ rp->priv->control_type, rp->name,
+ NULL, &zone_ops[rd->id], nr_pl,
+ &constraint_ops);
+ if (IS_ERR(power_zone)) {
+ pr_debug("failed to register power zone %s\n",
+ rp->name);
+ return PTR_ERR(power_zone);
+ }
+ /* track parent zone in per package/socket data */
+ rp->power_zone = power_zone;
+ /* done, only one package domain per socket */
+ break;
+ }
+ }
+ if (!power_zone) {
+ pr_err("no package domain found, unknown topology!\n");
+ return -ENODEV;
+ }
+ /* now register domains as children of the socket/package */
+ for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
+ if (rd->id == RAPL_DOMAIN_PACKAGE)
+ continue;
+ /* number of power limits per domain varies */
+ nr_pl = find_nr_power_limit(rd);
+ power_zone = powercap_register_zone(&rd->power_zone,
+ rp->priv->control_type,
+ rd->name, rp->power_zone,
+ &zone_ops[rd->id], nr_pl,
+ &constraint_ops);
+
+ if (IS_ERR(power_zone)) {
+ pr_debug("failed to register power_zone, %s:%s\n",
+ rp->name, rd->name);
+ ret = PTR_ERR(power_zone);
+ goto err_cleanup;
+ }
+ }
+ return 0;
+
+err_cleanup:
+ /*
+ * Clean up previously initialized domains within the package if we
+ * failed after the first domain setup.
+ */
+ while (--rd >= rp->domains) {
+ pr_debug("unregister %s domain %s\n", rp->name, rd->name);
+ powercap_unregister_zone(rp->priv->control_type,
+ &rd->power_zone);
+ }
+
+ return ret;
+}
+
+int rapl_add_platform_domain(struct rapl_priv *priv)
+{
+ struct rapl_domain *rd;
+ struct powercap_zone *power_zone;
+ struct reg_action ra;
+ int ret;
+
+ ra.reg = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
+ ra.mask = ~0;
+ ret = priv->read_raw(0, &ra);
+ if (ret || !ra.value)
+ return -ENODEV;
+
+ ra.reg = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
+ ra.mask = ~0;
+ ret = priv->read_raw(0, &ra);
+ if (ret || !ra.value)
+ return -ENODEV;
+
+ rd = kzalloc(sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
+
+ rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
+ rd->id = RAPL_DOMAIN_PLATFORM;
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] =
+ priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
+ rd->regs[RAPL_DOMAIN_REG_STATUS] =
+ priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
+ rd->rpl[0].prim_id = PL1_ENABLE;
+ rd->rpl[0].name = pl1_name;
+ rd->rpl[1].prim_id = PL2_ENABLE;
+ rd->rpl[1].name = pl2_name;
+ rd->rp = rapl_find_package_domain(0, priv);
+
+ power_zone = powercap_register_zone(&rd->power_zone, priv->control_type,
+ "psys", NULL,
+ &zone_ops[RAPL_DOMAIN_PLATFORM],
+ 2, &constraint_ops);
+
+ if (IS_ERR(power_zone)) {
+ kfree(rd);
+ return PTR_ERR(power_zone);
+ }
+
+ priv->platform_rapl_domain = rd;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rapl_add_platform_domain);
+
+void rapl_remove_platform_domain(struct rapl_priv *priv)
+{
+ if (priv->platform_rapl_domain) {
+ powercap_unregister_zone(priv->control_type,
+ &priv->platform_rapl_domain->power_zone);
+ kfree(priv->platform_rapl_domain);
+ }
+}
+EXPORT_SYMBOL_GPL(rapl_remove_platform_domain);
+
+static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
+{
+ struct reg_action ra;
+
+ switch (domain) {
+ case RAPL_DOMAIN_PACKAGE:
+ case RAPL_DOMAIN_PP0:
+ case RAPL_DOMAIN_PP1:
+ case RAPL_DOMAIN_DRAM:
+ ra.reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS];
+ break;
+ case RAPL_DOMAIN_PLATFORM:
+ /* PSYS(PLATFORM) is not a CPU domain, so avoid printng error */
+ return -EINVAL;
+ default:
+ pr_err("invalid domain id %d\n", domain);
+ return -EINVAL;
+ }
+ /* make sure domain counters are available and contains non-zero
+ * values, otherwise skip it.
+ */
+
+ ra.mask = ~0;
+ if (rp->priv->read_raw(cpu, &ra) || !ra.value)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ * Check if power limits are available. Two cases when they are not available:
+ * 1. Locked by BIOS, in this case we still provide read-only access so that
+ * users can see what limit is set by the BIOS.
+ * 2. Some CPUs make some domains monitoring only which means PLx MSRs may not
+ * exist at all. In this case, we do not show the constraints in powercap.
+ *
+ * Called after domains are detected and initialized.
+ */
+static void rapl_detect_powerlimit(struct rapl_domain *rd)
+{
+ u64 val64;
+ int i;
+
+ /* check if the domain is locked by BIOS, ignore if MSR doesn't exist */
+ if (!rapl_read_data_raw(rd, FW_LOCK, false, &val64)) {
+ if (val64) {
+ pr_info("RAPL %s domain %s locked by BIOS\n",
+ rd->rp->name, rd->name);
+ rd->state |= DOMAIN_STATE_BIOS_LOCKED;
+ }
+ }
+ /* check if power limit MSR exists, otherwise domain is monitoring only */
+ for (i = 0; i < NR_POWER_LIMITS; i++) {
+ int prim = rd->rpl[i].prim_id;
+
+ if (rapl_read_data_raw(rd, prim, false, &val64))
+ rd->rpl[i].name = NULL;
+ }
+}
+
+/* Detect active and valid domains for the given CPU, caller must
+ * ensure the CPU belongs to the targeted package and CPU hotlug is disabled.
+ */
+static int rapl_detect_domains(struct rapl_package *rp, int cpu)
+{
+ struct rapl_domain *rd;
+ int i;
+
+ for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
+ /* use physical package id to read counters */
+ if (!rapl_check_domain(cpu, i, rp)) {
+ rp->domain_map |= 1 << i;
+ pr_info("Found RAPL domain %s\n", rapl_domain_names[i]);
+ }
+ }
+ rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX);
+ if (!rp->nr_domains) {
+ pr_debug("no valid rapl domains found in %s\n", rp->name);
+ return -ENODEV;
+ }
+ pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name);
+
+ rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain),
+ GFP_KERNEL);
+ if (!rp->domains)
+ return -ENOMEM;
+
+ rapl_init_domains(rp);
+
+ for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++)
+ rapl_detect_powerlimit(rd);
+
+ return 0;
+}
+
+/* called from CPU hotplug notifier, hotplug lock held */
+void rapl_remove_package(struct rapl_package *rp)
+{
+ struct rapl_domain *rd, *rd_package = NULL;
+
+ package_power_limit_irq_restore(rp);
+
+ for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
+ rapl_write_data_raw(rd, PL1_ENABLE, 0);
+ rapl_write_data_raw(rd, PL1_CLAMP, 0);
+ if (find_nr_power_limit(rd) > 1) {
+ rapl_write_data_raw(rd, PL2_ENABLE, 0);
+ rapl_write_data_raw(rd, PL2_CLAMP, 0);
+ }
+ if (rd->id == RAPL_DOMAIN_PACKAGE) {
+ rd_package = rd;
+ continue;
+ }
+ pr_debug("remove package, undo power limit on %s: %s\n",
+ rp->name, rd->name);
+ powercap_unregister_zone(rp->priv->control_type,
+ &rd->power_zone);
+ }
+ /* do parent zone last */
+ powercap_unregister_zone(rp->priv->control_type,
+ &rd_package->power_zone);
+ list_del(&rp->plist);
+ if (list_empty(&rapl_packages))
+ rapl_remove_core();
+ kfree(rp);
+}
+EXPORT_SYMBOL_GPL(rapl_remove_package);
+
+/* caller to ensure CPU hotplug lock is held */
+struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_priv *priv)
+{
+ int id = topology_logical_die_id(cpu);
+ struct rapl_package *rp;
+
+ list_for_each_entry(rp, &rapl_packages, plist) {
+ if (rp->id == id
+ && rp->priv->control_type == priv->control_type)
+ return rp;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rapl_find_package_domain);
+
+/* called from CPU hotplug notifier, hotplug lock held */
+struct rapl_package *rapl_add_package(int cpu, struct rapl_priv *priv)
+{
+ int id = topology_logical_die_id(cpu);
+ struct rapl_package *rp;
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ int ret;
+
+ ret = rapl_init_core();
+ if (ret)
+ return ERR_PTR(ret);
+
+ rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL);
+ if (!rp)
+ return ERR_PTR(-ENOMEM);
+
+ /* add the new package to the list */
+ rp->id = id;
+ rp->lead_cpu = cpu;
+ rp->priv = priv;
+
+ if (topology_max_die_per_package() > 1)
+ snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH,
+ "package-%d-die-%d", c->phys_proc_id, c->cpu_die_id);
+ else
+ snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d",
+ c->phys_proc_id);
+
+ /* check if the package contains valid domains */
+ if (rapl_detect_domains(rp, cpu) || rapl_defaults->check_unit(rp, cpu)) {
+ ret = -ENODEV;
+ goto err_free_package;
+ }
+ ret = rapl_package_register_powercap(rp);
+ if (!ret) {
+ INIT_LIST_HEAD(&rp->plist);
+ list_add(&rp->plist, &rapl_packages);
+ return rp;
+ }
+
+err_free_package:
+ kfree(rp->domains);
+ kfree(rp);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(rapl_add_package);
+
+static void power_limit_state_save(void)
+{
+ struct rapl_package *rp;
+ struct rapl_domain *rd;
+ int nr_pl, ret, i;
+
+ get_online_cpus();
+ list_for_each_entry(rp, &rapl_packages, plist) {
+ if (!rp->power_zone)
+ continue;
+ rd = power_zone_to_rapl_domain(rp->power_zone);
+ nr_pl = find_nr_power_limit(rd);
+ for (i = 0; i < nr_pl; i++) {
+ switch (rd->rpl[i].prim_id) {
+ case PL1_ENABLE:
+ ret = rapl_read_data_raw(rd,
+ POWER_LIMIT1, true,
+ &rd->rpl[i].last_power_limit);
+ if (ret)
+ rd->rpl[i].last_power_limit = 0;
+ break;
+ case PL2_ENABLE:
+ ret = rapl_read_data_raw(rd,
+ POWER_LIMIT2, true,
+ &rd->rpl[i].last_power_limit);
+ if (ret)
+ rd->rpl[i].last_power_limit = 0;
+ break;
+ }
+ }
+ }
+ put_online_cpus();
+}
+
+static void power_limit_state_restore(void)
+{
+ struct rapl_package *rp;
+ struct rapl_domain *rd;
+ int nr_pl, i;
+
+ get_online_cpus();
+ list_for_each_entry(rp, &rapl_packages, plist) {
+ if (!rp->power_zone)
+ continue;
+ rd = power_zone_to_rapl_domain(rp->power_zone);
+ nr_pl = find_nr_power_limit(rd);
+ for (i = 0; i < nr_pl; i++) {
+ switch (rd->rpl[i].prim_id) {
+ case PL1_ENABLE:
+ if (rd->rpl[i].last_power_limit)
+ rapl_write_data_raw(rd, POWER_LIMIT1,
+ rd->rpl[i].last_power_limit);
+ break;
+ case PL2_ENABLE:
+ if (rd->rpl[i].last_power_limit)
+ rapl_write_data_raw(rd, POWER_LIMIT2,
+ rd->rpl[i].last_power_limit);
+ break;
+ }
+ }
+ }
+ put_online_cpus();
+}
+
+static int rapl_pm_callback(struct notifier_block *nb,
+ unsigned long mode, void *_unused)
+{
+ switch (mode) {
+ case PM_SUSPEND_PREPARE:
+ power_limit_state_save();
+ break;
+ case PM_POST_SUSPEND:
+ power_limit_state_restore();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block rapl_pm_notifier = {
+ .notifier_call = rapl_pm_callback,
+};
+
+static int rapl_init_core(void)
+{
+ const struct x86_cpu_id *id;
+ int ret;
+
+ if (rapl_defaults)
+ return 0;
+
+ id = x86_match_cpu(rapl_ids);
+ if (!id) {
+ pr_err("driver does not support CPU family %d model %d\n",
+ boot_cpu_data.x86, boot_cpu_data.x86_model);
+
+ return -ENODEV;
+ }
+
+ rapl_defaults = (struct rapl_defaults *)id->driver_data;
+
+ ret = register_pm_notifier(&rapl_pm_notifier);
+
+ return 0;
+}
+
+static void rapl_remove_core(void)
+{
+ unregister_pm_notifier(&rapl_pm_notifier);
+ rapl_defaults = NULL;
+}
+
+MODULE_DESCRIPTION("Runtime Average Power Limit (RAPL) core");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
index 1a0df65..e83739f 100644
--- a/include/linux/intel_rapl.h
+++ b/include/linux/intel_rapl.h
@@ -128,4 +128,11 @@ struct rapl_package {
struct rapl_priv *priv;
};
+struct rapl_package *rapl_find_package_domain(int cpu, struct rapl_priv *priv);
+struct rapl_package *rapl_add_package(int cpu, struct rapl_priv *priv);
+void rapl_remove_package(struct rapl_package *rp);
+
+int rapl_add_platform_domain(struct rapl_priv *priv);
+void rapl_remove_platform_domain(struct rapl_priv *priv);
+
#endif /* __INTEL_RAPL_H__ */
--
2.7.4
^ permalink raw reply related
* [PATCH 13/13] intel_rapl: Fix module autoloading issue
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
intel_rapl driver used to have a list of cpuids, which is used to
1. check if the processor support RAPL MSRs
2. do some cpu model specific setting
3. module autoloading
Now, the cpu model specific setting are moved to intel_rapl_common.c as
part of the common code, because the setup is also needed by RAPL MMIO
interface on those platforms.
But removing the cpuid list from intel_rapl MSR interface driver results
in that the driver can not be loaded automatically.
Maintaining another copy of the cpuid list in intel_rapl.c does not make
sense because it increases the complexity when enabling RAPL support on a
new cpu model.
Fix the problem by creating an "intel_rapl_msr" platform device in the
common code, and make RAPL MSR interface driver (intel_rapl.c) probe the
platform device directly.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 24 +++++++++++++++----
drivers/powercap/intel_rapl_common.c | 45 ++++++++++++++++++++++--------------
2 files changed, 48 insertions(+), 21 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index ce0948a..1dd8008 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -19,6 +19,7 @@
#include <linux/powercap.h>
#include <linux/suspend.h>
#include <linux/intel_rapl.h>
+#include <linux/platform_device.h>
#include <asm/iosf_mbi.h>
#include <asm/processor.h>
@@ -122,7 +123,7 @@ static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
return ret;
}
-static int __init rapl_msr_init(void)
+static int rapl_msr_probe(struct platform_device *pdev)
{
int ret;
@@ -152,15 +153,30 @@ static int __init rapl_msr_init(void)
return ret;
}
-static void __exit rapl_msr_exit(void)
+static int rapl_msr_remove(struct platform_device *pdev)
{
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
rapl_remove_platform_domain(&rapl_msr_priv);
powercap_unregister_control_type(rapl_msr_priv.control_type);
+ return 0;
}
-module_init(rapl_msr_init);
-module_exit(rapl_msr_exit);
+static const struct platform_device_id rapl_msr_ids[] = {
+ { .name = "intel_rapl_msr", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, rapl_msr_ids);
+
+static struct platform_driver intel_rapl_msr_driver = {
+ .probe = rapl_msr_probe,
+ .remove = rapl_msr_remove,
+ .id_table = rapl_msr_ids,
+ .driver = {
+ .name = "intel_rapl_msr",
+ },
+};
+
+module_platform_driver(intel_rapl_msr_driver);
MODULE_DESCRIPTION("Driver for Intel RAPL (Running Average Power Limit)");
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>");
diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c
index ef6a9d0..9f94997 100644
--- a/drivers/powercap/intel_rapl_common.c
+++ b/drivers/powercap/intel_rapl_common.c
@@ -18,10 +18,11 @@
#include <linux/cpu.h>
#include <linux/powercap.h>
#include <linux/suspend.h>
-#include <asm/iosf_mbi.h>
#include <linux/intel_rapl.h>
-
#include <linux/processor.h>
+#include <linux/platform_device.h>
+
+#include <asm/iosf_mbi.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
@@ -136,8 +137,6 @@ static int rapl_write_data_raw(struct rapl_domain *rd,
static u64 rapl_unit_xlate(struct rapl_domain *rd,
enum unit_type type, u64 value, int to_raw);
static void package_power_limit_irq_save(struct rapl_package *rp);
-static int rapl_init_core(void);
-static void rapl_remove_core(void);
static LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */
@@ -1262,8 +1261,6 @@ void rapl_remove_package(struct rapl_package *rp)
powercap_unregister_zone(rp->priv->control_type,
&rd_package->power_zone);
list_del(&rp->plist);
- if (list_empty(&rapl_packages))
- rapl_remove_core();
kfree(rp);
}
EXPORT_SYMBOL_GPL(rapl_remove_package);
@@ -1292,10 +1289,6 @@ struct rapl_package *rapl_add_package(int cpu, struct rapl_priv *priv)
struct cpuinfo_x86 *c = &cpu_data(cpu);
int ret;
- ret = rapl_init_core();
- if (ret)
- return ERR_PTR(ret);
-
rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL);
if (!rp)
return ERR_PTR(-ENOMEM);
@@ -1413,14 +1406,13 @@ static struct notifier_block rapl_pm_notifier = {
.notifier_call = rapl_pm_callback,
};
-static int rapl_init_core(void)
+static struct platform_device *rapl_msr_platdev;
+
+static int __init rapl_init(void)
{
const struct x86_cpu_id *id;
int ret;
- if (rapl_defaults)
- return 0;
-
id = x86_match_cpu(rapl_ids);
if (!id) {
pr_err("driver does not support CPU family %d model %d\n",
@@ -1432,15 +1424,34 @@ static int rapl_init_core(void)
rapl_defaults = (struct rapl_defaults *)id->driver_data;
ret = register_pm_notifier(&rapl_pm_notifier);
+ if (ret)
+ return ret;
- return 0;
+ rapl_msr_platdev = platform_device_alloc("intel_rapl_msr", 0);
+ if (!rapl_msr_platdev) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ret = platform_device_add(rapl_msr_platdev);
+ if (ret)
+ platform_device_put(rapl_msr_platdev);
+
+end:
+ if (ret)
+ unregister_pm_notifier(&rapl_pm_notifier);
+
+ return ret;
}
-static void rapl_remove_core(void)
+static void __exit rapl_exit(void)
{
+ platform_device_unregister(rapl_msr_platdev);
unregister_pm_notifier(&rapl_pm_notifier);
- rapl_defaults = NULL;
}
+module_init(rapl_init);
+module_exit(rapl_exit);
+
MODULE_DESCRIPTION("Runtime Average Power Limit (RAPL) core");
MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH 12/13] int340X/processor_thermal_device: add support for MMIO RAPL
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
Introduce MMIO RAPL support as Intel processor_thermal device exposes the
capability to do RAPL control via MMIO registers.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/thermal/intel/int340x_thermal/Kconfig | 5 +
.../int340x_thermal/processor_thermal_device.c | 174 ++++++++++++++++++++-
2 files changed, 173 insertions(+), 6 deletions(-)
diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig
index 5333e01..547a35f 100644
--- a/drivers/thermal/intel/int340x_thermal/Kconfig
+++ b/drivers/thermal/intel/int340x_thermal/Kconfig
@@ -40,4 +40,9 @@ config INT3406_THERMAL
brightness in order to address a thermal condition or to reduce
power consumed by display device.
+config PROC_THERMAL_MMIO_RAPL
+ bool
+ depends on 64BIT
+ select INTEL_RAPL_CORE
+ default y
endif
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
index 53c84fa..5410c07 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -11,6 +11,8 @@
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
+#include <linux/cpuhotplug.h>
+#include <linux/intel_rapl.h>
#include "int340x_thermal_zone.h"
#include "../intel_soc_dts_iosf.h"
@@ -37,6 +39,8 @@
/* GeminiLake thermal reporting device */
#define PCI_DEVICE_ID_PROC_GLK_THERMAL 0x318C
+#define DRV_NAME "proc_thermal"
+
struct power_config {
u32 index;
u32 min_uw;
@@ -52,6 +56,7 @@ struct proc_thermal_device {
struct power_config power_limits[2];
struct int34x_thermal_zone *int340x_zone;
struct intel_soc_dts_sensors *soc_dts;
+ void __iomem *mmio_base;
};
enum proc_thermal_emum_mode_type {
@@ -60,6 +65,12 @@ enum proc_thermal_emum_mode_type {
PROC_THERMAL_PLATFORM_DEV
};
+struct rapl_mmio_regs {
+ u64 reg_unit;
+ u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
+ int limits[RAPL_DOMAIN_MAX];
+};
+
/*
* We can have only one type of enumeration, PCI or Platform,
* not both. So we don't need instance specific data.
@@ -367,8 +378,152 @@ static irqreturn_t proc_thermal_pci_msi_irq(int irq, void *devid)
return IRQ_HANDLED;
}
+#ifdef CONFIG_PROC_THERMAL_MMIO_RAPL
+
+#define MCHBAR 0
+
+/* RAPL Support via MMIO interface */
+static struct rapl_priv rapl_mmio_priv;
+
+static int rapl_mmio_cpu_online(unsigned int cpu)
+{
+ struct rapl_package *rp;
+
+ /* mmio rapl supports package 0 only for now */
+ if (topology_physical_package_id(cpu))
+ return 0;
+
+ rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
+ if (!rp) {
+ rp = rapl_add_package(cpu, &rapl_mmio_priv);
+ if (IS_ERR(rp))
+ return PTR_ERR(rp);
+ }
+ cpumask_set_cpu(cpu, &rp->cpumask);
+ return 0;
+}
+
+static int rapl_mmio_cpu_down_prep(unsigned int cpu)
+{
+ struct rapl_package *rp;
+ int lead_cpu;
+
+ rp = rapl_find_package_domain(cpu, &rapl_mmio_priv);
+ if (!rp)
+ return 0;
+
+ cpumask_clear_cpu(cpu, &rp->cpumask);
+ lead_cpu = cpumask_first(&rp->cpumask);
+ if (lead_cpu >= nr_cpu_ids)
+ rapl_remove_package(rp);
+ else if (rp->lead_cpu == cpu)
+ rp->lead_cpu = lead_cpu;
+ return 0;
+}
+
+static int rapl_mmio_read_raw(int cpu, struct reg_action *ra)
+{
+ if (!ra->reg)
+ return -EINVAL;
+
+ ra->value = readq((void *)ra->reg);
+ ra->value &= ra->mask;
+ return 0;
+}
+
+static int rapl_mmio_write_raw(int cpu, struct reg_action *ra)
+{
+ u64 val;
+
+ if (!ra->reg)
+ return -EINVAL;
+
+ val = readq((void *)ra->reg);
+ val &= ~ra->mask;
+ val |= ra->value;
+ writeq(val, (void *)ra->reg);
+ return 0;
+}
+
+static int proc_thermal_rapl_add(struct pci_dev *pdev,
+ struct proc_thermal_device *proc_priv,
+ struct rapl_mmio_regs *rapl_regs)
+{
+ enum rapl_domain_reg_id reg;
+ enum rapl_domain_type domain;
+ u64 resource_start, resource_len;
+ int ret;
+
+ if (!rapl_regs)
+ return 0;
+
+ ret = pcim_iomap_regions(pdev, 1 << MCHBAR, DRV_NAME);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+ return -ENOMEM;
+ }
+
+ proc_priv->mmio_base = pcim_iomap_table(pdev)[MCHBAR];
+
+ for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX; domain++) {
+ for (reg = RAPL_DOMAIN_REG_LIMIT; reg < RAPL_DOMAIN_REG_MAX; reg++)
+ if (rapl_regs->regs[domain][reg])
+ rapl_mmio_priv.regs[domain][reg] =
+ (u64)proc_priv->mmio_base +
+ rapl_regs->regs[domain][reg];
+ rapl_mmio_priv.limits[domain] = rapl_regs->limits[domain];
+ }
+ rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base + rapl_regs->reg_unit;
+
+ rapl_mmio_priv.read_raw = rapl_mmio_read_raw;
+ rapl_mmio_priv.write_raw = rapl_mmio_write_raw;
+
+ rapl_mmio_priv.control_type = powercap_register_control_type(NULL, "intel-rapl-mmio", NULL);
+ if (IS_ERR(rapl_mmio_priv.control_type)) {
+ pr_debug("failed to register powercap control_type.\n");
+ return PTR_ERR(rapl_mmio_priv.control_type);
+ }
+
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
+ rapl_mmio_cpu_online, rapl_mmio_cpu_down_prep);
+ if (ret < 0) {
+ powercap_unregister_control_type(rapl_mmio_priv.control_type);
+ return ret;
+ }
+ rapl_mmio_priv.pcap_rapl_online = ret;
+
+ return 0;
+}
+
+static void proc_thermal_rapl_remove(void)
+{
+ cpuhp_remove_state(rapl_mmio_priv.pcap_rapl_online);
+ powercap_unregister_control_type(rapl_mmio_priv.control_type);
+}
+
+static const struct rapl_mmio_regs rapl_mmio_hsw = {
+ .reg_unit = 0x5938,
+ .regs[RAPL_DOMAIN_PACKAGE] = { 0x59a0, 0x593c, 0x58f0, 0, 0x5930},
+ .regs[RAPL_DOMAIN_DRAM] = { 0x58e0, 0x58e8, 0x58ec, 0, 0},
+ .limits[RAPL_DOMAIN_PACKAGE] = 2,
+ .limits[RAPL_DOMAIN_DRAM] = 2,
+};
+
+#else
+
+static int proc_thermal_rapl_add(struct pci_dev *pdev,
+ struct proc_thermal_device *proc_priv,
+ struct rapl_mmio_regs *rapl_regs)
+{
+ return 0;
+}
+static void proc_thermal_rapl_remove(void) {}
+static const struct rapl_mmio_regs rapl_mmio_hsw;
+
+#endif /* CONFIG_MMIO_RAPL */
+
static int proc_thermal_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *unused)
+ const struct pci_device_id *id)
{
struct proc_thermal_device *proc_priv;
int ret;
@@ -378,15 +533,21 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev,
return -ENODEV;
}
- ret = pci_enable_device(pdev);
+ ret = pcim_enable_device(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "error: could not enable device\n");
return ret;
}
ret = proc_thermal_add(&pdev->dev, &proc_priv);
+ if (ret)
+ return ret;
+
+ ret = proc_thermal_rapl_add(pdev, proc_priv,
+ (struct rapl_mmio_regs *)id->driver_data);
if (ret) {
- pci_disable_device(pdev);
+ dev_err(&pdev->dev, "failed to add RAPL MMIO interface\n");
+ proc_thermal_remove(proc_priv);
return ret;
}
@@ -439,14 +600,15 @@ static void proc_thermal_pci_remove(struct pci_dev *pdev)
pci_disable_msi(pdev);
}
}
+ proc_thermal_rapl_remove();
proc_thermal_remove(proc_priv);
- pci_disable_device(pdev);
}
static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL)},
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL),
+ .driver_data = (kernel_ulong_t)&rapl_mmio_hsw, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT0_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)},
@@ -461,7 +623,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
static struct pci_driver proc_thermal_pci_driver = {
- .name = "proc_thermal",
+ .name = DRV_NAME,
.probe = proc_thermal_pci_probe,
.remove = proc_thermal_pci_remove,
.id_table = proc_thermal_pci_ids,
--
2.7.4
^ permalink raw reply related
* [PATCH 10/13] intel_rapl: support 64 bit register
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
RAPL MMIO interface uses 64 bit registers, thus force use 64 bit register
for all the RAPL code.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 11 +++++++----
drivers/powercap/intel_rapl_common.c | 6 +++---
include/linux/intel_rapl.h | 8 ++++----
3 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 67130c4..8868624 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -83,8 +83,10 @@ static int rapl_cpu_down_prep(unsigned int cpu)
static int rapl_msr_read_raw(int cpu, struct reg_action *ra)
{
- if (rdmsrl_safe_on_cpu(cpu, ra->reg, &ra->value)) {
- pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg, cpu);
+ u32 msr = (u32)ra->reg;
+
+ if (rdmsrl_safe_on_cpu(cpu, msr, &ra->value)) {
+ pr_debug("failed to read msr 0x%x on cpu %d\n", msr, cpu);
return -EIO;
}
ra->value &= ra->mask;
@@ -94,16 +96,17 @@ static int rapl_msr_read_raw(int cpu, struct reg_action *ra)
static void rapl_msr_update_func(void *info)
{
struct reg_action *ra = info;
+ u32 msr = (u32)ra->reg;
u64 val;
- ra->err = rdmsrl_safe(ra->reg, &val);
+ ra->err = rdmsrl_safe(msr, &val);
if (ra->err)
return;
val &= ~ra->mask;
val |= ra->value;
- ra->err = wrmsrl_safe(ra->reg, val);
+ ra->err = wrmsrl_safe(msr, val);
}
static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c
index 8f589cf..f11bac7 100644
--- a/drivers/powercap/intel_rapl_common.c
+++ b/drivers/powercap/intel_rapl_common.c
@@ -689,7 +689,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
ra.mask = rp->mask;
if (rd->rp->priv->read_raw(cpu, &ra)) {
- pr_debug("failed to read reg 0x%x on cpu %d\n", ra.reg, cpu);
+ pr_debug("failed to read reg 0x%llx on cpu %d\n", ra.reg, cpu);
return -EIO;
}
@@ -749,7 +749,7 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
ra.reg = rp->priv->reg_unit;
ra.mask = ~0;
if (rp->priv->read_raw(cpu, &ra)) {
- pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
+ pr_err("Failed to read power unit REG 0x%llx on CPU %d, exit.\n",
rp->priv->reg_unit, cpu);
return -ENODEV;
}
@@ -777,7 +777,7 @@ static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
ra.reg = rp->priv->reg_unit;
ra.mask = ~0;
if (rp->priv->read_raw(cpu, &ra)) {
- pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
+ pr_err("Failed to read power unit REG 0x%llx on CPU %d, exit.\n",
rp->priv->reg_unit, cpu);
return -ENODEV;
}
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
index e83739f..1ca0f69 100644
--- a/include/linux/intel_rapl.h
+++ b/include/linux/intel_rapl.h
@@ -78,7 +78,7 @@ struct rapl_package;
struct rapl_domain {
const char *name;
enum rapl_domain_type id;
- int regs[RAPL_DOMAIN_REG_MAX];
+ u64 regs[RAPL_DOMAIN_REG_MAX];
struct powercap_zone power_zone;
struct rapl_domain_data rdd;
struct rapl_power_limit rpl[NR_POWER_LIMITS];
@@ -89,7 +89,7 @@ struct rapl_domain {
};
struct reg_action {
- u32 reg;
+ u64 reg;
u64 mask;
u64 value;
int err;
@@ -99,8 +99,8 @@ struct rapl_priv {
struct powercap_control_type *control_type;
struct rapl_domain *platform_rapl_domain;
enum cpuhp_state pcap_rapl_online;
- u32 reg_unit;
- u32 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
+ u64 reg_unit;
+ u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
int (*read_raw)(int cpu, struct reg_action *ra);
int (*write_raw)(int cpu, struct reg_action *ra);
};
--
2.7.4
^ permalink raw reply related
* [PATCH 11/13] intel_rapl: support two power limits for every RAPL domain
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
RAPL MSR interface supports 2 power limits for package domain, and 1 power
limit for other domains, while RAPL MMIO interface supports 2 power limits
for both package and dram domains.
And when 2 power limits are supported, the FW_LOCK bit is in bit 63 of the
register, instead of bit 31.
Remove the assumption that only pakcage domain supports 2 power limits.
And allow the RAPL interface driver to specify the number of power limits
supported, for every single RAPL domain it owns..
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 1 +
drivers/powercap/intel_rapl_common.c | 72 +++++++++++++-----------------------
include/linux/intel_rapl.h | 1 +
3 files changed, 27 insertions(+), 47 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 8868624..ce0948a 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -40,6 +40,7 @@ static struct rapl_priv rapl_msr_priv = {
MSR_DRAM_POWER_LIMIT, MSR_DRAM_ENERGY_STATUS, MSR_DRAM_PERF_STATUS, 0, MSR_DRAM_POWER_INFO },
.regs[RAPL_DOMAIN_PLATFORM] = {
MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0},
+ .limits[RAPL_DOMAIN_PACKAGE] = 2,
};
/* Handles CPU hotplug on multi-socket systems.
diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c
index f11bac7..ef6a9d0 100644
--- a/drivers/powercap/intel_rapl_common.c
+++ b/drivers/powercap/intel_rapl_common.c
@@ -38,8 +38,8 @@
#define POWER_LIMIT2_MASK (0x7FFFULL<<32)
#define POWER_LIMIT2_ENABLE BIT_ULL(47)
#define POWER_LIMIT2_CLAMP BIT_ULL(48)
-#define POWER_PACKAGE_LOCK BIT_ULL(63)
-#define POWER_PP_LOCK BIT(31)
+#define POWER_HIGH_LOCK BIT_ULL(63)
+#define POWER_LOW_LOCK BIT(31)
#define TIME_WINDOW1_MASK (0x7FULL<<17)
#define TIME_WINDOW2_MASK (0x7FULL<<49)
@@ -513,60 +513,38 @@ static const struct powercap_zone_constraint_ops constraint_ops = {
/* called after domain detection and package level data are set */
static void rapl_init_domains(struct rapl_package *rp)
{
- int i;
+ enum rapl_domain_type i;
+ enum rapl_domain_reg_id j;
struct rapl_domain *rd = rp->domains;
for (i = 0; i < RAPL_DOMAIN_MAX; i++) {
unsigned int mask = rp->domain_map & (1 << i);
- rd->regs[RAPL_DOMAIN_REG_LIMIT] =
- rp->priv->regs[i][RAPL_DOMAIN_REG_LIMIT];
- rd->regs[RAPL_DOMAIN_REG_STATUS] =
- rp->priv->regs[i][RAPL_DOMAIN_REG_STATUS];
- rd->regs[RAPL_DOMAIN_REG_PERF] =
- rp->priv->regs[i][RAPL_DOMAIN_REG_PERF];
- rd->regs[RAPL_DOMAIN_REG_POLICY] =
- rp->priv->regs[i][RAPL_DOMAIN_REG_POLICY];
- rd->regs[RAPL_DOMAIN_REG_INFO] =
- rp->priv->regs[i][RAPL_DOMAIN_REG_INFO];
-
- switch (mask) {
- case BIT(RAPL_DOMAIN_PACKAGE):
- rd->name = rapl_domain_names[RAPL_DOMAIN_PACKAGE];
- rd->id = RAPL_DOMAIN_PACKAGE;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
+ if (!mask)
+ continue;
+
+ rd->rp = rp;
+ rd->name = rapl_domain_names[i];
+ rd->id = i;
+ rd->rpl[0].prim_id = PL1_ENABLE;
+ rd->rpl[0].name = pl1_name;
+ /* some domain may support two power limits */
+ if (rp->priv->limits[i] == 2) {
rd->rpl[1].prim_id = PL2_ENABLE;
rd->rpl[1].name = pl2_name;
- break;
- case BIT(RAPL_DOMAIN_PP0):
- rd->name = rapl_domain_names[RAPL_DOMAIN_PP0];
- rd->id = RAPL_DOMAIN_PP0;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- break;
- case BIT(RAPL_DOMAIN_PP1):
- rd->name = rapl_domain_names[RAPL_DOMAIN_PP1];
- rd->id = RAPL_DOMAIN_PP1;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
- break;
- case BIT(RAPL_DOMAIN_DRAM):
- rd->name = rapl_domain_names[RAPL_DOMAIN_DRAM];
- rd->id = RAPL_DOMAIN_DRAM;
- rd->rpl[0].prim_id = PL1_ENABLE;
- rd->rpl[0].name = pl1_name;
+ }
+
+ for (j = 0; j < RAPL_DOMAIN_REG_MAX; j++)
+ rd->regs[j] = rp->priv->regs[i][j];
+
+ if (i == RAPL_DOMAIN_DRAM) {
rd->domain_energy_unit =
rapl_defaults->dram_domain_energy_unit;
if (rd->domain_energy_unit)
pr_info("DRAM domain energy unit %dpj\n",
rd->domain_energy_unit);
- break;
- }
- if (mask) {
- rd->rp = rp;
- rd++;
}
+ rd++;
}
}
@@ -613,7 +591,7 @@ static struct rapl_primitive_info rpi[] = {
RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32,
RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0),
- PRIMITIVE_INFO_INIT(FW_LOCK, POWER_PP_LOCK, 31,
+ PRIMITIVE_INFO_INIT(FW_LOCK, POWER_LOW_LOCK, 31,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15,
RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0),
@@ -675,9 +653,9 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
cpu = rd->rp->lead_cpu;
- /* special-case package domain, which uses a different bit */
- if (prim == FW_LOCK && rd->id == RAPL_DOMAIN_PACKAGE) {
- rp->mask = POWER_PACKAGE_LOCK;
+ /* domain with 2 limits has different bit */
+ if (prim == FW_LOCK && rd->rp->priv->limits[rd->id] == 2) {
+ rp->mask = POWER_HIGH_LOCK;
rp->shift = 63;
}
/* non-hardware data are collected by the polling thread */
diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h
index 1ca0f69..2f470d2 100644
--- a/include/linux/intel_rapl.h
+++ b/include/linux/intel_rapl.h
@@ -101,6 +101,7 @@ struct rapl_priv {
enum cpuhp_state pcap_rapl_online;
u64 reg_unit;
u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
+ int limits[RAPL_DOMAIN_MAX];
int (*read_raw)(int cpu, struct reg_action *ra);
int (*write_raw)(int cpu, struct reg_action *ra);
};
--
2.7.4
^ permalink raw reply related
* [PATCH 08/13] intel_rapl: cleanup hardcoded MSR access
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
There are still some places in the common code that have hardcoded
MSR access, convert them to follow the abstracted register access.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 38 ++++++++++++++++++++++----------------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index fbf91dd5..e476bd1 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -766,22 +766,24 @@ static int rapl_write_data_raw(struct rapl_domain *rd,
*/
static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
{
- u64 msr_val;
+ struct reg_action ra;
u32 value;
- if (rdmsrl_safe_on_cpu(cpu, rp->priv->reg_unit, &msr_val)) {
- pr_err("Failed to read power unit MSR 0x%x on CPU %d, exit.\n",
+ ra.reg = rp->priv->reg_unit;
+ ra.mask = ~0;
+ if (rp->priv->read_raw(cpu, &ra)) {
+ pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
rp->priv->reg_unit, cpu);
return -ENODEV;
}
- value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
+ value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value);
- value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
+ value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
rp->power_unit = 1000000 / (1 << value);
- value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
+ value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
rp->time_unit = 1000000 / (1 << value);
pr_debug("Core CPU %s energy=%dpJ, time=%dus, power=%duW\n",
@@ -792,21 +794,24 @@ static int rapl_check_unit_core(struct rapl_package *rp, int cpu)
static int rapl_check_unit_atom(struct rapl_package *rp, int cpu)
{
- u64 msr_val;
+ struct reg_action ra;
u32 value;
- if (rdmsrl_safe_on_cpu(cpu, rp->priv->reg_unit, &msr_val)) {
- pr_err("Failed to read power unit MSR 0x%x on CPU %d, exit.\n",
+ ra.reg = rp->priv->reg_unit;
+ ra.mask = ~0;
+ if (rp->priv->read_raw(cpu, &ra)) {
+ pr_err("Failed to read power unit REG 0x%x on CPU %d, exit.\n",
rp->priv->reg_unit, cpu);
return -ENODEV;
}
- value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
+
+ value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value;
- value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
+ value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
rp->power_unit = (1 << value) * 1000;
- value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
+ value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
rp->time_unit = 1000000 / (1 << value);
pr_debug("Atom %s energy=%dpJ, time=%dus, power=%duW\n",
@@ -1179,15 +1184,14 @@ static void rapl_remove_platform_domain(struct rapl_priv *priv)
static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
{
- u32 reg;
- u64 val = 0;
+ struct reg_action ra;
switch (domain) {
case RAPL_DOMAIN_PACKAGE:
case RAPL_DOMAIN_PP0:
case RAPL_DOMAIN_PP1:
case RAPL_DOMAIN_DRAM:
- reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS];
+ ra.reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS];
break;
case RAPL_DOMAIN_PLATFORM:
/* PSYS(PLATFORM) is not a CPU domain, so avoid printng error */
@@ -1199,7 +1203,9 @@ static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
/* make sure domain counters are available and contains non-zero
* values, otherwise skip it.
*/
- if (rdmsrl_safe_on_cpu(cpu, reg, &val) || !val)
+
+ ra.mask = ~0;
+ if (rp->priv->read_raw(cpu, &ra) || !ra.value)
return -ENODEV;
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH 07/13] intel_rapl: cleanup some functions
From: Zhang Rui @ 2019-06-25 15:16 UTC (permalink / raw)
To: rjw; +Cc: linux-pm, srinivas.pandruvada, rui.zhang
In-Reply-To: <1561475808-24839-1-git-send-email-rui.zhang@intel.com>
Previously, there are three functions:
rapl_register_psys(), which registers platform rapl domain.
rapl_register_powercap(), which registers powercap control type.
rapl_unregsiter_powercap(), which unregisters platform rapl domain and
powercap control type.
This is confusing as the function name does not describe what it does
clearly.
With this patch, the three functions are removed, and two new functions
rapl_register_platform_domain()/rapl_unregister_platform_domain() are
introduced instead, and they do exactly what their function name describes.
Plus, as part of the common code, hardcoded MSR accesses in these functions
are converted to follow the abstracted register access.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
drivers/powercap/intel_rapl.c | 62 +++++++++++++++++++++----------------------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index 7dc9965..fbf91dd5 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -1051,16 +1051,6 @@ static void rapl_update_domain_data(struct rapl_package *rp)
}
-static void rapl_unregister_powercap(void)
-{
- if (&rapl_msr_priv.platform_rapl_domain) {
- powercap_unregister_zone(rapl_msr_priv.control_type,
- &rapl_msr_priv.platform_rapl_domain->power_zone);
- kfree(rapl_msr_priv.platform_rapl_domain);
- }
- powercap_unregister_control_type(rapl_msr_priv.control_type);
-}
-
static int rapl_package_register_powercap(struct rapl_package *rp)
{
struct rapl_domain *rd;
@@ -1130,16 +1120,23 @@ static int rapl_package_register_powercap(struct rapl_package *rp)
return ret;
}
-static int __init rapl_register_psys(void)
+static int __init rapl_add_platform_domain(struct rapl_priv *priv)
{
struct rapl_domain *rd;
struct powercap_zone *power_zone;
- u64 val;
+ struct reg_action ra;
+ int ret;
- if (rdmsrl_safe_on_cpu(0, rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS], &val) || !val)
+ ra.reg = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
+ ra.mask = ~0;
+ ret = priv->read_raw(0, &ra);
+ if (ret || !ra.value)
return -ENODEV;
- if (rdmsrl_safe_on_cpu(0, rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT], &val) || !val)
+ ra.reg = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
+ ra.mask = ~0;
+ ret = priv->read_raw(0, &ra);
+ if (ret || !ra.value)
return -ENODEV;
rd = kzalloc(sizeof(*rd), GFP_KERNEL);
@@ -1148,15 +1145,15 @@ static int __init rapl_register_psys(void)
rd->name = rapl_domain_names[RAPL_DOMAIN_PLATFORM];
rd->id = RAPL_DOMAIN_PLATFORM;
- rd->regs[RAPL_DOMAIN_REG_LIMIT] = rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
- rd->regs[RAPL_DOMAIN_REG_STATUS] = rapl_msr_priv.regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
+ rd->regs[RAPL_DOMAIN_REG_LIMIT] = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT];
+ rd->regs[RAPL_DOMAIN_REG_STATUS] = priv->regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS];
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
rd->rpl[1].prim_id = PL2_ENABLE;
rd->rpl[1].name = pl2_name;
- rd->rp = rapl_find_package_domain(0, &rapl_msr_priv);
+ rd->rp = rapl_find_package_domain(0, priv);
- power_zone = powercap_register_zone(&rd->power_zone, rapl_msr_priv.control_type,
+ power_zone = powercap_register_zone(&rd->power_zone, priv->control_type,
"psys", NULL,
&zone_ops[RAPL_DOMAIN_PLATFORM],
2, &constraint_ops);
@@ -1166,19 +1163,18 @@ static int __init rapl_register_psys(void)
return PTR_ERR(power_zone);
}
- rapl_msr_priv.platform_rapl_domain = rd;
+ priv->platform_rapl_domain = rd;
return 0;
}
-static int __init rapl_register_powercap(void)
+static void rapl_remove_platform_domain(struct rapl_priv *priv)
{
- rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
- if (IS_ERR(rapl_msr_priv.control_type)) {
- pr_debug("failed to register powercap control_type.\n");
- return PTR_ERR(rapl_msr_priv.control_type);
+ if (priv->platform_rapl_domain) {
+ powercap_unregister_zone(priv->control_type,
+ &priv->platform_rapl_domain->power_zone);
+ kfree(priv->platform_rapl_domain);
}
- return 0;
}
static int rapl_check_domain(int cpu, int domain, struct rapl_package *rp)
@@ -1526,9 +1522,12 @@ static int __init rapl_init(void)
rapl_msr_priv.read_raw = rapl_msr_read_raw;
rapl_msr_priv.write_raw = rapl_msr_write_raw;
- ret = rapl_register_powercap();
- if (ret)
- return ret;
+
+ rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
+ if (IS_ERR(rapl_msr_priv.control_type)) {
+ pr_debug("failed to register powercap control_type.\n");
+ return PTR_ERR(rapl_msr_priv.control_type);
+ }
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
rapl_cpu_online, rapl_cpu_down_prep);
@@ -1537,7 +1536,7 @@ static int __init rapl_init(void)
rapl_msr_priv.pcap_rapl_online = ret;
/* Don't bail out if PSys is not supported */
- rapl_register_psys();
+ rapl_add_platform_domain(&rapl_msr_priv);
ret = register_pm_notifier(&rapl_pm_notifier);
if (ret)
@@ -1549,7 +1548,7 @@ static int __init rapl_init(void)
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
err_unreg:
- rapl_unregister_powercap();
+ powercap_unregister_control_type(rapl_msr_priv.control_type);
return ret;
}
@@ -1557,7 +1556,8 @@ static void __exit rapl_exit(void)
{
unregister_pm_notifier(&rapl_pm_notifier);
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
- rapl_unregister_powercap();
+ rapl_remove_platform_domain(&rapl_msr_priv);
+ powercap_unregister_control_type(rapl_msr_priv.control_type);
}
module_init(rapl_init);
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox