Commit 2d5c80bc authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

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
parents 5b564207 0a398945
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -655,7 +655,7 @@ static int acpi_idle_enter(struct cpuidle_device *dev,
	return index;
}

static void acpi_idle_enter_s2idle(struct cpuidle_device *dev,
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,
+10 −0
Original line number Diff line number Diff line
@@ -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
+2 −3
Original line number Diff line number Diff line
@@ -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

+51 −23
Original line number Diff line number Diff line
@@ -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,7 +47,7 @@ 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,
static int psci_pd_parse_state_nodes(struct genpd_power_state *states,
				     int state_count)
{
	int i, ret;
@@ -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);
}
+80 −61
Original line number Diff line number Diff line
@@ -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,7 +152,7 @@ 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,
static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
				     struct psci_cpuidle_data *data,
				     unsigned int state_count, int cpu)
{
@@ -190,7 +175,7 @@ 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,
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)
{
@@ -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,31 +199,25 @@ 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,
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;
@@ -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);
Loading