Merge branch 'pm-cpuidle'
* pm-cpuidle: cpuidle: ACPI: fix 'return' with no value build warning intel_idle: Customize IceLake server support cpuidle: change enter_s2idle() prototype cpuidle: psci: Prevent domain idlestates until consumers are ready cpuidle: psci: Convert PM domain to platform driver cpuidle: psci: Fix error path via converting to a platform driver cpuidle: psci: Fail cpuidle registration if set OSI mode failed cpuidle: psci: Split into two separate build objects intel_idle: Eliminate redundant static variable
This commit is contained in:
commit
2d5c80bcf8
|
@ -655,8 +655,8 @@ static int acpi_idle_enter(struct cpuidle_device *dev,
|
|||
return index;
|
||||
}
|
||||
|
||||
static void acpi_idle_enter_s2idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
static int acpi_idle_enter_s2idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
|
||||
|
||||
|
@ -664,16 +664,18 @@ static void acpi_idle_enter_s2idle(struct cpuidle_device *dev,
|
|||
struct acpi_processor *pr = __this_cpu_read(processors);
|
||||
|
||||
if (unlikely(!pr))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (pr->flags.bm_check) {
|
||||
acpi_idle_enter_bm(pr, cx, false);
|
||||
return;
|
||||
return 0;
|
||||
} else {
|
||||
ACPI_FLUSH_CPU_CACHE();
|
||||
}
|
||||
}
|
||||
acpi_idle_do_entry(cx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
|
||||
|
|
|
@ -23,6 +23,16 @@ config ARM_PSCI_CPUIDLE
|
|||
It provides an idle driver that is capable of detecting and
|
||||
managing idle states through the PSCI firmware interface.
|
||||
|
||||
config ARM_PSCI_CPUIDLE_DOMAIN
|
||||
bool "PSCI CPU idle Domain"
|
||||
depends on ARM_PSCI_CPUIDLE
|
||||
depends on PM_GENERIC_DOMAINS_OF
|
||||
default y
|
||||
help
|
||||
Select this to enable the PSCI based CPUidle driver to use PM domains,
|
||||
which is needed to support the hierarchical DT based layout of the
|
||||
idle states.
|
||||
|
||||
config ARM_BIG_LITTLE_CPUIDLE
|
||||
bool "Support for ARM big.LITTLE processors"
|
||||
depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS || COMPILE_TEST
|
||||
|
|
|
@ -21,9 +21,8 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o
|
|||
obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
|
||||
obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
|
||||
obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.o
|
||||
obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle_psci.o
|
||||
cpuidle_psci-y := cpuidle-psci.o
|
||||
cpuidle_psci-$(CONFIG_PM_GENERIC_DOMAINS_OF) += cpuidle-psci-domain.o
|
||||
obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle-psci.o
|
||||
obj-$(CONFIG_ARM_PSCI_CPUIDLE_DOMAIN) += cpuidle-psci-domain.o
|
||||
obj-$(CONFIG_ARM_TEGRA_CPUIDLE) += cpuidle-tegra.o
|
||||
obj-$(CONFIG_ARM_QCOM_SPM_CPUIDLE) += cpuidle-qcom-spm.o
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/cpu.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/psci.h>
|
||||
|
@ -26,7 +27,7 @@ struct psci_pd_provider {
|
|||
};
|
||||
|
||||
static LIST_HEAD(psci_pd_providers);
|
||||
static bool osi_mode_enabled __initdata;
|
||||
static bool psci_pd_allow_domain_state;
|
||||
|
||||
static int psci_pd_power_off(struct generic_pm_domain *pd)
|
||||
{
|
||||
|
@ -36,6 +37,9 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
|
|||
if (!state->data)
|
||||
return 0;
|
||||
|
||||
if (!psci_pd_allow_domain_state)
|
||||
return -EBUSY;
|
||||
|
||||
/* OSI mode is enabled, set the corresponding domain state. */
|
||||
pd_state = state->data;
|
||||
psci_set_domain_state(*pd_state);
|
||||
|
@ -43,8 +47,8 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init psci_pd_parse_state_nodes(struct genpd_power_state *states,
|
||||
int state_count)
|
||||
static int psci_pd_parse_state_nodes(struct genpd_power_state *states,
|
||||
int state_count)
|
||||
{
|
||||
int i, ret;
|
||||
u32 psci_state, *psci_state_buf;
|
||||
|
@ -73,7 +77,7 @@ free_state:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __init psci_pd_parse_states(struct device_node *np,
|
||||
static int psci_pd_parse_states(struct device_node *np,
|
||||
struct genpd_power_state **states, int *state_count)
|
||||
{
|
||||
int ret;
|
||||
|
@ -101,7 +105,7 @@ static void psci_pd_free_states(struct genpd_power_state *states,
|
|||
kfree(states);
|
||||
}
|
||||
|
||||
static int __init psci_pd_init(struct device_node *np)
|
||||
static int psci_pd_init(struct device_node *np)
|
||||
{
|
||||
struct generic_pm_domain *pd;
|
||||
struct psci_pd_provider *pd_provider;
|
||||
|
@ -168,7 +172,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __init psci_pd_remove(void)
|
||||
static void psci_pd_remove(void)
|
||||
{
|
||||
struct psci_pd_provider *pd_provider, *it;
|
||||
struct generic_pm_domain *genpd;
|
||||
|
@ -186,7 +190,7 @@ static void __init psci_pd_remove(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int __init psci_pd_init_topology(struct device_node *np, bool add)
|
||||
static int psci_pd_init_topology(struct device_node *np, bool add)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct of_phandle_args child, parent;
|
||||
|
@ -212,24 +216,33 @@ static int __init psci_pd_init_topology(struct device_node *np, bool add)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init psci_pd_add_topology(struct device_node *np)
|
||||
static int psci_pd_add_topology(struct device_node *np)
|
||||
{
|
||||
return psci_pd_init_topology(np, true);
|
||||
}
|
||||
|
||||
static void __init psci_pd_remove_topology(struct device_node *np)
|
||||
static void psci_pd_remove_topology(struct device_node *np)
|
||||
{
|
||||
psci_pd_init_topology(np, false);
|
||||
}
|
||||
|
||||
static const struct of_device_id psci_of_match[] __initconst = {
|
||||
static void psci_cpuidle_domain_sync_state(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* All devices have now been attached/probed to the PM domain topology,
|
||||
* hence it's fine to allow domain states to be picked.
|
||||
*/
|
||||
psci_pd_allow_domain_state = true;
|
||||
}
|
||||
|
||||
static const struct of_device_id psci_of_match[] = {
|
||||
{ .compatible = "arm,psci-1.0" },
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init psci_idle_init_domains(void)
|
||||
static int psci_cpuidle_domain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = of_find_matching_node(NULL, psci_of_match);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *node;
|
||||
int ret = 0, pd_count = 0;
|
||||
|
||||
|
@ -238,7 +251,7 @@ static int __init psci_idle_init_domains(void)
|
|||
|
||||
/* Currently limit the hierarchical topology to be used in OSI mode. */
|
||||
if (!psci_has_osi_support())
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Parse child nodes for the "#power-domain-cells" property and
|
||||
|
@ -257,7 +270,7 @@ static int __init psci_idle_init_domains(void)
|
|||
|
||||
/* Bail out if not using the hierarchical CPU topology. */
|
||||
if (!pd_count)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
/* Link genpd masters/subdomains to model the CPU topology. */
|
||||
ret = psci_pd_add_topology(np);
|
||||
|
@ -272,10 +285,8 @@ static int __init psci_idle_init_domains(void)
|
|||
goto remove_pd;
|
||||
}
|
||||
|
||||
osi_mode_enabled = true;
|
||||
of_node_put(np);
|
||||
pr_info("Initialized CPU PM domain topology\n");
|
||||
return pd_count;
|
||||
return 0;
|
||||
|
||||
put_node:
|
||||
of_node_put(node);
|
||||
|
@ -283,19 +294,28 @@ remove_pd:
|
|||
if (pd_count)
|
||||
psci_pd_remove();
|
||||
pr_err("failed to create CPU PM domains ret=%d\n", ret);
|
||||
out:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver psci_cpuidle_domain_driver = {
|
||||
.probe = psci_cpuidle_domain_probe,
|
||||
.driver = {
|
||||
.name = "psci-cpuidle-domain",
|
||||
.of_match_table = psci_of_match,
|
||||
.sync_state = psci_cpuidle_domain_sync_state,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init psci_idle_init_domains(void)
|
||||
{
|
||||
return platform_driver_register(&psci_cpuidle_domain_driver);
|
||||
}
|
||||
subsys_initcall(psci_idle_init_domains);
|
||||
|
||||
struct device __init *psci_dt_attach_cpu(int cpu)
|
||||
struct device *psci_dt_attach_cpu(int cpu)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
if (!osi_mode_enabled)
|
||||
return NULL;
|
||||
|
||||
dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci");
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return dev;
|
||||
|
@ -306,3 +326,11 @@ struct device __init *psci_dt_attach_cpu(int cpu)
|
|||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void psci_dt_detach_cpu(struct device *dev)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return;
|
||||
|
||||
dev_pm_domain_detach(dev, false);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
|
||||
|
@ -33,7 +35,7 @@ struct psci_cpuidle_data {
|
|||
|
||||
static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);
|
||||
static DEFINE_PER_CPU(u32, domain_state);
|
||||
static bool psci_cpuidle_use_cpuhp __initdata;
|
||||
static bool psci_cpuidle_use_cpuhp;
|
||||
|
||||
void psci_set_domain_state(u32 state)
|
||||
{
|
||||
|
@ -104,7 +106,7 @@ static int psci_idle_cpuhp_down(unsigned int cpu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void __init psci_idle_init_cpuhp(void)
|
||||
static void psci_idle_init_cpuhp(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
@ -127,30 +129,13 @@ static int psci_enter_idle_state(struct cpuidle_device *dev,
|
|||
return psci_enter_state(idx, state[idx]);
|
||||
}
|
||||
|
||||
static struct cpuidle_driver psci_idle_driver __initdata = {
|
||||
.name = "psci_idle",
|
||||
.owner = THIS_MODULE,
|
||||
/*
|
||||
* PSCI idle states relies on architectural WFI to
|
||||
* be represented as state index 0.
|
||||
*/
|
||||
.states[0] = {
|
||||
.enter = psci_enter_idle_state,
|
||||
.exit_latency = 1,
|
||||
.target_residency = 1,
|
||||
.power_usage = UINT_MAX,
|
||||
.name = "WFI",
|
||||
.desc = "ARM WFI",
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id psci_idle_state_match[] __initconst = {
|
||||
static const struct of_device_id psci_idle_state_match[] = {
|
||||
{ .compatible = "arm,idle-state",
|
||||
.data = psci_enter_idle_state },
|
||||
{ },
|
||||
};
|
||||
|
||||
int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
|
||||
int psci_dt_parse_state_node(struct device_node *np, u32 *state)
|
||||
{
|
||||
int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
|
||||
|
||||
|
@ -167,9 +152,9 @@ int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
|
||||
struct psci_cpuidle_data *data,
|
||||
unsigned int state_count, int cpu)
|
||||
static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
|
||||
struct psci_cpuidle_data *data,
|
||||
unsigned int state_count, int cpu)
|
||||
{
|
||||
/* Currently limit the hierarchical topology to be used in OSI mode. */
|
||||
if (!psci_has_osi_support())
|
||||
|
@ -190,9 +175,9 @@ static int __init psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
|
||||
struct device_node *cpu_node,
|
||||
unsigned int state_count, int cpu)
|
||||
static int psci_dt_cpu_init_idle(struct device *dev, struct cpuidle_driver *drv,
|
||||
struct device_node *cpu_node,
|
||||
unsigned int state_count, int cpu)
|
||||
{
|
||||
int i, ret = 0;
|
||||
u32 *psci_states;
|
||||
|
@ -200,7 +185,8 @@ static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
|
|||
struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
|
||||
|
||||
state_count++; /* Add WFI state too */
|
||||
psci_states = kcalloc(state_count, sizeof(*psci_states), GFP_KERNEL);
|
||||
psci_states = devm_kcalloc(dev, state_count, sizeof(*psci_states),
|
||||
GFP_KERNEL);
|
||||
if (!psci_states)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -213,32 +199,26 @@ static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
|
|||
of_node_put(state_node);
|
||||
|
||||
if (ret)
|
||||
goto free_mem;
|
||||
return ret;
|
||||
|
||||
pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
|
||||
}
|
||||
|
||||
if (i != state_count) {
|
||||
ret = -ENODEV;
|
||||
goto free_mem;
|
||||
}
|
||||
if (i != state_count)
|
||||
return -ENODEV;
|
||||
|
||||
/* Initialize optional data, used for the hierarchical topology. */
|
||||
ret = psci_dt_cpu_init_topology(drv, data, state_count, cpu);
|
||||
if (ret < 0)
|
||||
goto free_mem;
|
||||
return ret;
|
||||
|
||||
/* Idle states parsed correctly, store them in the per-cpu struct. */
|
||||
data->psci_states = psci_states;
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
kfree(psci_states);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __init int psci_cpu_init_idle(struct cpuidle_driver *drv,
|
||||
unsigned int cpu, unsigned int state_count)
|
||||
static int psci_cpu_init_idle(struct device *dev, struct cpuidle_driver *drv,
|
||||
unsigned int cpu, unsigned int state_count)
|
||||
{
|
||||
struct device_node *cpu_node;
|
||||
int ret;
|
||||
|
@ -254,14 +234,22 @@ static __init int psci_cpu_init_idle(struct cpuidle_driver *drv,
|
|||
if (!cpu_node)
|
||||
return -ENODEV;
|
||||
|
||||
ret = psci_dt_cpu_init_idle(drv, cpu_node, state_count, cpu);
|
||||
ret = psci_dt_cpu_init_idle(dev, drv, cpu_node, state_count, cpu);
|
||||
|
||||
of_node_put(cpu_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init psci_idle_init_cpu(int cpu)
|
||||
static void psci_cpu_deinit_idle(int cpu)
|
||||
{
|
||||
struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
|
||||
|
||||
psci_dt_detach_cpu(data->dev);
|
||||
psci_cpuidle_use_cpuhp = false;
|
||||
}
|
||||
|
||||
static int psci_idle_init_cpu(struct device *dev, int cpu)
|
||||
{
|
||||
struct cpuidle_driver *drv;
|
||||
struct device_node *cpu_node;
|
||||
|
@ -284,17 +272,26 @@ static int __init psci_idle_init_cpu(int cpu)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
drv = kmemdup(&psci_idle_driver, sizeof(*drv), GFP_KERNEL);
|
||||
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
drv->name = "psci_idle";
|
||||
drv->owner = THIS_MODULE;
|
||||
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
|
||||
|
||||
/*
|
||||
* Initialize idle states data, starting at index 1, since
|
||||
* by default idle state 0 is the quiescent state reached
|
||||
* by the cpu by executing the wfi instruction.
|
||||
*
|
||||
* PSCI idle states relies on architectural WFI to be represented as
|
||||
* state index 0.
|
||||
*/
|
||||
drv->states[0].enter = psci_enter_idle_state;
|
||||
drv->states[0].exit_latency = 1;
|
||||
drv->states[0].target_residency = 1;
|
||||
drv->states[0].power_usage = UINT_MAX;
|
||||
strcpy(drv->states[0].name, "WFI");
|
||||
strcpy(drv->states[0].desc, "ARM WFI");
|
||||
|
||||
/*
|
||||
* If no DT idle states are detected (ret == 0) let the driver
|
||||
* initialization fail accordingly since there is no reason to
|
||||
* initialize the idle driver if only wfi is supported, the
|
||||
|
@ -302,48 +299,45 @@ static int __init psci_idle_init_cpu(int cpu)
|
|||
* on idle entry.
|
||||
*/
|
||||
ret = dt_init_idle_driver(drv, psci_idle_state_match, 1);
|
||||
if (ret <= 0) {
|
||||
ret = ret ? : -ENODEV;
|
||||
goto out_kfree_drv;
|
||||
}
|
||||
if (ret <= 0)
|
||||
return ret ? : -ENODEV;
|
||||
|
||||
/*
|
||||
* Initialize PSCI idle states.
|
||||
*/
|
||||
ret = psci_cpu_init_idle(drv, cpu, ret);
|
||||
ret = psci_cpu_init_idle(dev, drv, cpu, ret);
|
||||
if (ret) {
|
||||
pr_err("CPU %d failed to PSCI idle\n", cpu);
|
||||
goto out_kfree_drv;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpuidle_register(drv, NULL);
|
||||
if (ret)
|
||||
goto out_kfree_drv;
|
||||
goto deinit;
|
||||
|
||||
cpuidle_cooling_register(drv);
|
||||
|
||||
return 0;
|
||||
|
||||
out_kfree_drv:
|
||||
kfree(drv);
|
||||
deinit:
|
||||
psci_cpu_deinit_idle(cpu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* psci_idle_init - Initializes PSCI cpuidle driver
|
||||
* psci_idle_probe - Initializes PSCI cpuidle driver
|
||||
*
|
||||
* Initializes PSCI cpuidle driver for all CPUs, if any CPU fails
|
||||
* to register cpuidle driver then rollback to cancel all CPUs
|
||||
* registration.
|
||||
*/
|
||||
static int __init psci_idle_init(void)
|
||||
static int psci_cpuidle_probe(struct platform_device *pdev)
|
||||
{
|
||||
int cpu, ret;
|
||||
struct cpuidle_driver *drv;
|
||||
struct cpuidle_device *dev;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
ret = psci_idle_init_cpu(cpu);
|
||||
ret = psci_idle_init_cpu(&pdev->dev, cpu);
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
}
|
||||
|
@ -356,9 +350,34 @@ out_fail:
|
|||
dev = per_cpu(cpuidle_devices, cpu);
|
||||
drv = cpuidle_get_cpu_driver(dev);
|
||||
cpuidle_unregister(drv);
|
||||
kfree(drv);
|
||||
psci_cpu_deinit_idle(cpu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver psci_cpuidle_driver = {
|
||||
.probe = psci_cpuidle_probe,
|
||||
.driver = {
|
||||
.name = "psci-cpuidle",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init psci_idle_init(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&psci_cpuidle_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
platform_driver_unregister(&psci_cpuidle_driver);
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(psci_idle_init);
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
#ifndef __CPUIDLE_PSCI_H
|
||||
#define __CPUIDLE_PSCI_H
|
||||
|
||||
struct device;
|
||||
struct device_node;
|
||||
|
||||
void psci_set_domain_state(u32 state);
|
||||
int __init psci_dt_parse_state_node(struct device_node *np, u32 *state);
|
||||
int psci_dt_parse_state_node(struct device_node *np, u32 *state);
|
||||
|
||||
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
|
||||
struct device __init *psci_dt_attach_cpu(int cpu);
|
||||
#ifdef CONFIG_ARM_PSCI_CPUIDLE_DOMAIN
|
||||
struct device *psci_dt_attach_cpu(int cpu);
|
||||
void psci_dt_detach_cpu(struct device *dev);
|
||||
#else
|
||||
static inline struct device __init *psci_dt_attach_cpu(int cpu) { return NULL; }
|
||||
static inline struct device *psci_dt_attach_cpu(int cpu) { return NULL; }
|
||||
static inline void psci_dt_detach_cpu(struct device *dev) { }
|
||||
#endif
|
||||
|
||||
#endif /* __CPUIDLE_PSCI_H */
|
||||
|
|
|
@ -253,11 +253,13 @@ static int tegra_cpuidle_enter(struct cpuidle_device *dev,
|
|||
return err ? -1 : index;
|
||||
}
|
||||
|
||||
static void tegra114_enter_s2idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
static int tegra114_enter_s2idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
tegra_cpuidle_enter(dev, drv, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -66,8 +66,6 @@ static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
|
|||
static unsigned long auto_demotion_disable_flags;
|
||||
static bool disable_promotion_to_c1e;
|
||||
|
||||
static bool lapic_timer_always_reliable;
|
||||
|
||||
struct idle_cpu {
|
||||
struct cpuidle_state *state_table;
|
||||
|
||||
|
@ -142,7 +140,7 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev,
|
|||
if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED)
|
||||
leave_mm(cpu);
|
||||
|
||||
if (!static_cpu_has(X86_FEATURE_ARAT) && !lapic_timer_always_reliable) {
|
||||
if (!static_cpu_has(X86_FEATURE_ARAT)) {
|
||||
/*
|
||||
* Switch over to one-shot tick broadcast if the target C-state
|
||||
* is deeper than C1.
|
||||
|
@ -175,13 +173,15 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev,
|
|||
* Invoked as a suspend-to-idle callback routine with frozen user space, frozen
|
||||
* scheduler tick and suspended scheduler clock on the target CPU.
|
||||
*/
|
||||
static __cpuidle void intel_idle_s2idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
static __cpuidle int intel_idle_s2idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
unsigned long eax = flg2MWAIT(drv->states[index].flags);
|
||||
unsigned long ecx = 1; /* break on interrupt flag */
|
||||
|
||||
mwait_idle_with_hints(eax, ecx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -752,6 +752,35 @@ static struct cpuidle_state skx_cstates[] __initdata = {
|
|||
.enter = NULL }
|
||||
};
|
||||
|
||||
static struct cpuidle_state icx_cstates[] __initdata = {
|
||||
{
|
||||
.name = "C1",
|
||||
.desc = "MWAIT 0x00",
|
||||
.flags = MWAIT2flg(0x00),
|
||||
.exit_latency = 1,
|
||||
.target_residency = 1,
|
||||
.enter = &intel_idle,
|
||||
.enter_s2idle = intel_idle_s2idle, },
|
||||
{
|
||||
.name = "C1E",
|
||||
.desc = "MWAIT 0x01",
|
||||
.flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE,
|
||||
.exit_latency = 4,
|
||||
.target_residency = 4,
|
||||
.enter = &intel_idle,
|
||||
.enter_s2idle = intel_idle_s2idle, },
|
||||
{
|
||||
.name = "C6",
|
||||
.desc = "MWAIT 0x20",
|
||||
.flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED,
|
||||
.exit_latency = 128,
|
||||
.target_residency = 384,
|
||||
.enter = &intel_idle,
|
||||
.enter_s2idle = intel_idle_s2idle, },
|
||||
{
|
||||
.enter = NULL }
|
||||
};
|
||||
|
||||
static struct cpuidle_state atom_cstates[] __initdata = {
|
||||
{
|
||||
.name = "C1E",
|
||||
|
@ -1056,6 +1085,12 @@ static const struct idle_cpu idle_cpu_skx __initconst = {
|
|||
.use_acpi = true,
|
||||
};
|
||||
|
||||
static const struct idle_cpu idle_cpu_icx __initconst = {
|
||||
.state_table = icx_cstates,
|
||||
.disable_promotion_to_c1e = true,
|
||||
.use_acpi = true,
|
||||
};
|
||||
|
||||
static const struct idle_cpu idle_cpu_avn __initconst = {
|
||||
.state_table = avn_cstates,
|
||||
.disable_promotion_to_c1e = true,
|
||||
|
@ -1110,6 +1145,7 @@ static const struct x86_cpu_id intel_idle_ids[] __initconst = {
|
|||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, &idle_cpu_skl),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &idle_cpu_skl),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, &idle_cpu_skx),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &idle_cpu_icx),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNL, &idle_cpu_knl),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNM, &idle_cpu_knl),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &idle_cpu_bxt),
|
||||
|
@ -1562,7 +1598,7 @@ static int intel_idle_cpu_online(unsigned int cpu)
|
|||
{
|
||||
struct cpuidle_device *dev;
|
||||
|
||||
if (!lapic_timer_always_reliable)
|
||||
if (!boot_cpu_has(X86_FEATURE_ARAT))
|
||||
tick_broadcast_enable();
|
||||
|
||||
/*
|
||||
|
@ -1655,16 +1691,13 @@ static int __init intel_idle_init(void)
|
|||
goto init_driver_fail;
|
||||
}
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */
|
||||
lapic_timer_always_reliable = true;
|
||||
|
||||
retval = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "idle/intel:online",
|
||||
intel_idle_cpu_online, NULL);
|
||||
if (retval < 0)
|
||||
goto hp_setup_fail;
|
||||
|
||||
pr_debug("Local APIC timer is reliable in %s\n",
|
||||
lapic_timer_always_reliable ? "all C-states" : "C1");
|
||||
boot_cpu_has(X86_FEATURE_ARAT) ? "all C-states" : "C1");
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -65,10 +65,13 @@ struct cpuidle_state {
|
|||
* CPUs execute ->enter_s2idle with the local tick or entire timekeeping
|
||||
* suspended, so it must not re-enable interrupts at any point (even
|
||||
* temporarily) or attempt to change states of clock event devices.
|
||||
*
|
||||
* This callback may point to the same function as ->enter if all of
|
||||
* the above requirements are met by it.
|
||||
*/
|
||||
void (*enter_s2idle) (struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index);
|
||||
int (*enter_s2idle)(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index);
|
||||
};
|
||||
|
||||
/* Idle State Flags */
|
||||
|
|
Loading…
Reference in New Issue