181d549e0SLorenzo Pieralisi // SPDX-License-Identifier: GPL-2.0-only
281d549e0SLorenzo Pieralisi /*
381d549e0SLorenzo Pieralisi  * PSCI CPU idle driver.
481d549e0SLorenzo Pieralisi  *
581d549e0SLorenzo Pieralisi  * Copyright (C) 2019 ARM Ltd.
681d549e0SLorenzo Pieralisi  * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
781d549e0SLorenzo Pieralisi  */
881d549e0SLorenzo Pieralisi 
981d549e0SLorenzo Pieralisi #define pr_fmt(fmt) "CPUidle PSCI: " fmt
1081d549e0SLorenzo Pieralisi 
119c6ceecbSUlf Hansson #include <linux/cpuhotplug.h>
12fc7a3d9eSDaniel Lezcano #include <linux/cpu_cooling.h>
1381d549e0SLorenzo Pieralisi #include <linux/cpuidle.h>
1481d549e0SLorenzo Pieralisi #include <linux/cpumask.h>
1581d549e0SLorenzo Pieralisi #include <linux/cpu_pm.h>
1681d549e0SLorenzo Pieralisi #include <linux/kernel.h>
1781d549e0SLorenzo Pieralisi #include <linux/module.h>
1881d549e0SLorenzo Pieralisi #include <linux/of.h>
1981d549e0SLorenzo Pieralisi #include <linux/of_device.h>
20166bf835SUlf Hansson #include <linux/platform_device.h>
2181d549e0SLorenzo Pieralisi #include <linux/psci.h>
22*670c90deSUlf Hansson #include <linux/pm_domain.h>
23ce85aef5SUlf Hansson #include <linux/pm_runtime.h>
2481d549e0SLorenzo Pieralisi #include <linux/slab.h>
25166bf835SUlf Hansson #include <linux/string.h>
2681d549e0SLorenzo Pieralisi 
2781d549e0SLorenzo Pieralisi #include <asm/cpuidle.h>
2881d549e0SLorenzo Pieralisi 
298554951aSUlf Hansson #include "cpuidle-psci.h"
3081d549e0SLorenzo Pieralisi #include "dt_idle_states.h"
3181d549e0SLorenzo Pieralisi 
328554951aSUlf Hansson struct psci_cpuidle_data {
338554951aSUlf Hansson 	u32 *psci_states;
348554951aSUlf Hansson 	struct device *dev;
358554951aSUlf Hansson };
368554951aSUlf Hansson 
378554951aSUlf Hansson static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);
38a0cf3194SUlf Hansson static DEFINE_PER_CPU(u32, domain_state);
39166bf835SUlf Hansson static bool psci_cpuidle_use_cpuhp;
40a0cf3194SUlf Hansson 
41a65a397fSUlf Hansson void psci_set_domain_state(u32 state)
42a0cf3194SUlf Hansson {
43a0cf3194SUlf Hansson 	__this_cpu_write(domain_state, state);
44a0cf3194SUlf Hansson }
45a0cf3194SUlf Hansson 
46a0cf3194SUlf Hansson static inline u32 psci_get_domain_state(void)
47a0cf3194SUlf Hansson {
48a0cf3194SUlf Hansson 	return __this_cpu_read(domain_state);
49a0cf3194SUlf Hansson }
50a0cf3194SUlf Hansson 
51a0cf3194SUlf Hansson static inline int psci_enter_state(int idx, u32 state)
52a0cf3194SUlf Hansson {
53a0cf3194SUlf Hansson 	return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter, idx, state);
54a0cf3194SUlf Hansson }
55a0cf3194SUlf Hansson 
56*670c90deSUlf Hansson static int __psci_enter_domain_idle_state(struct cpuidle_device *dev,
57*670c90deSUlf Hansson 					  struct cpuidle_driver *drv, int idx,
58*670c90deSUlf Hansson 					  bool s2idle)
59a0cf3194SUlf Hansson {
60a0cf3194SUlf Hansson 	struct psci_cpuidle_data *data = this_cpu_ptr(&psci_cpuidle_data);
61a0cf3194SUlf Hansson 	u32 *states = data->psci_states;
62ce85aef5SUlf Hansson 	struct device *pd_dev = data->dev;
63ce85aef5SUlf Hansson 	u32 state;
64a0cf3194SUlf Hansson 	int ret;
65a0cf3194SUlf Hansson 
668b7ce5e4SUlf Hansson 	ret = cpu_pm_enter();
678b7ce5e4SUlf Hansson 	if (ret)
688b7ce5e4SUlf Hansson 		return -1;
698b7ce5e4SUlf Hansson 
70ce85aef5SUlf Hansson 	/* Do runtime PM to manage a hierarchical CPU toplogy. */
71*670c90deSUlf Hansson 	rcu_irq_enter_irqson();
72*670c90deSUlf Hansson 	if (s2idle)
73*670c90deSUlf Hansson 		dev_pm_genpd_suspend(pd_dev);
74*670c90deSUlf Hansson 	else
75*670c90deSUlf Hansson 		pm_runtime_put_sync_suspend(pd_dev);
76*670c90deSUlf Hansson 	rcu_irq_exit_irqson();
77ce85aef5SUlf Hansson 
78ce85aef5SUlf Hansson 	state = psci_get_domain_state();
79a0cf3194SUlf Hansson 	if (!state)
80a0cf3194SUlf Hansson 		state = states[idx];
81a0cf3194SUlf Hansson 
828b7ce5e4SUlf Hansson 	ret = psci_cpu_suspend_enter(state) ? -1 : idx;
83a0cf3194SUlf Hansson 
84*670c90deSUlf Hansson 	rcu_irq_enter_irqson();
85*670c90deSUlf Hansson 	if (s2idle)
86*670c90deSUlf Hansson 		dev_pm_genpd_resume(pd_dev);
87*670c90deSUlf Hansson 	else
88*670c90deSUlf Hansson 		pm_runtime_get_sync(pd_dev);
89*670c90deSUlf Hansson 	rcu_irq_exit_irqson();
90ce85aef5SUlf Hansson 
918b7ce5e4SUlf Hansson 	cpu_pm_exit();
928b7ce5e4SUlf Hansson 
93a0cf3194SUlf Hansson 	/* Clear the domain state to start fresh when back from idle. */
94a0cf3194SUlf Hansson 	psci_set_domain_state(0);
95a0cf3194SUlf Hansson 	return ret;
96a0cf3194SUlf Hansson }
979ffeb6d0SLorenzo Pieralisi 
98*670c90deSUlf Hansson static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
99*670c90deSUlf Hansson 					struct cpuidle_driver *drv, int idx)
100*670c90deSUlf Hansson {
101*670c90deSUlf Hansson 	return __psci_enter_domain_idle_state(dev, drv, idx, false);
102*670c90deSUlf Hansson }
103*670c90deSUlf Hansson 
104*670c90deSUlf Hansson static int psci_enter_s2idle_domain_idle_state(struct cpuidle_device *dev,
105*670c90deSUlf Hansson 					       struct cpuidle_driver *drv,
106*670c90deSUlf Hansson 					       int idx)
107*670c90deSUlf Hansson {
108*670c90deSUlf Hansson 	return __psci_enter_domain_idle_state(dev, drv, idx, true);
109*670c90deSUlf Hansson }
110*670c90deSUlf Hansson 
1119c6ceecbSUlf Hansson static int psci_idle_cpuhp_up(unsigned int cpu)
1129c6ceecbSUlf Hansson {
1139c6ceecbSUlf Hansson 	struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
1149c6ceecbSUlf Hansson 
1159c6ceecbSUlf Hansson 	if (pd_dev)
1169c6ceecbSUlf Hansson 		pm_runtime_get_sync(pd_dev);
1179c6ceecbSUlf Hansson 
1189c6ceecbSUlf Hansson 	return 0;
1199c6ceecbSUlf Hansson }
1209c6ceecbSUlf Hansson 
1219c6ceecbSUlf Hansson static int psci_idle_cpuhp_down(unsigned int cpu)
1229c6ceecbSUlf Hansson {
1239c6ceecbSUlf Hansson 	struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev);
1249c6ceecbSUlf Hansson 
1259c6ceecbSUlf Hansson 	if (pd_dev) {
1269c6ceecbSUlf Hansson 		pm_runtime_put_sync(pd_dev);
1279c6ceecbSUlf Hansson 		/* Clear domain state to start fresh at next online. */
1289c6ceecbSUlf Hansson 		psci_set_domain_state(0);
1299c6ceecbSUlf Hansson 	}
1309c6ceecbSUlf Hansson 
1319c6ceecbSUlf Hansson 	return 0;
1329c6ceecbSUlf Hansson }
1339c6ceecbSUlf Hansson 
134166bf835SUlf Hansson static void psci_idle_init_cpuhp(void)
1359c6ceecbSUlf Hansson {
1369c6ceecbSUlf Hansson 	int err;
1379c6ceecbSUlf Hansson 
1389c6ceecbSUlf Hansson 	if (!psci_cpuidle_use_cpuhp)
1399c6ceecbSUlf Hansson 		return;
1409c6ceecbSUlf Hansson 
1419c6ceecbSUlf Hansson 	err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING,
1429c6ceecbSUlf Hansson 					"cpuidle/psci:online",
1439c6ceecbSUlf Hansson 					psci_idle_cpuhp_up,
1449c6ceecbSUlf Hansson 					psci_idle_cpuhp_down);
1459c6ceecbSUlf Hansson 	if (err)
1469c6ceecbSUlf Hansson 		pr_warn("Failed %d while setup cpuhp state\n", err);
1479c6ceecbSUlf Hansson }
1489c6ceecbSUlf Hansson 
14981d549e0SLorenzo Pieralisi static int psci_enter_idle_state(struct cpuidle_device *dev,
15081d549e0SLorenzo Pieralisi 				struct cpuidle_driver *drv, int idx)
15181d549e0SLorenzo Pieralisi {
1528554951aSUlf Hansson 	u32 *state = __this_cpu_read(psci_cpuidle_data.psci_states);
1539ffeb6d0SLorenzo Pieralisi 
154a0cf3194SUlf Hansson 	return psci_enter_state(idx, state[idx]);
15581d549e0SLorenzo Pieralisi }
15681d549e0SLorenzo Pieralisi 
157166bf835SUlf Hansson static const struct of_device_id psci_idle_state_match[] = {
15881d549e0SLorenzo Pieralisi 	{ .compatible = "arm,idle-state",
15981d549e0SLorenzo Pieralisi 	  .data = psci_enter_idle_state },
16081d549e0SLorenzo Pieralisi 	{ },
16181d549e0SLorenzo Pieralisi };
16281d549e0SLorenzo Pieralisi 
163166bf835SUlf Hansson int psci_dt_parse_state_node(struct device_node *np, u32 *state)
1649ffeb6d0SLorenzo Pieralisi {
1659ffeb6d0SLorenzo Pieralisi 	int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
1669ffeb6d0SLorenzo Pieralisi 
1679ffeb6d0SLorenzo Pieralisi 	if (err) {
1689ffeb6d0SLorenzo Pieralisi 		pr_warn("%pOF missing arm,psci-suspend-param property\n", np);
1699ffeb6d0SLorenzo Pieralisi 		return err;
1709ffeb6d0SLorenzo Pieralisi 	}
1719ffeb6d0SLorenzo Pieralisi 
1729ffeb6d0SLorenzo Pieralisi 	if (!psci_power_state_is_valid(*state)) {
1739ffeb6d0SLorenzo Pieralisi 		pr_warn("Invalid PSCI power state %#x\n", *state);
1749ffeb6d0SLorenzo Pieralisi 		return -EINVAL;
1759ffeb6d0SLorenzo Pieralisi 	}
1769ffeb6d0SLorenzo Pieralisi 
1779ffeb6d0SLorenzo Pieralisi 	return 0;
1789ffeb6d0SLorenzo Pieralisi }
1799ffeb6d0SLorenzo Pieralisi 
180166bf835SUlf Hansson static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
1817fbee48eSUlf Hansson 				     struct psci_cpuidle_data *data,
1827fbee48eSUlf Hansson 				     unsigned int state_count, int cpu)
1837fbee48eSUlf Hansson {
1847fbee48eSUlf Hansson 	/* Currently limit the hierarchical topology to be used in OSI mode. */
1857fbee48eSUlf Hansson 	if (!psci_has_osi_support())
1867fbee48eSUlf Hansson 		return 0;
1877fbee48eSUlf Hansson 
1887fbee48eSUlf Hansson 	data->dev = psci_dt_attach_cpu(cpu);
1897fbee48eSUlf Hansson 	if (IS_ERR_OR_NULL(data->dev))
1907fbee48eSUlf Hansson 		return PTR_ERR_OR_ZERO(data->dev);
1917fbee48eSUlf Hansson 
1927fbee48eSUlf Hansson 	/*
1937fbee48eSUlf Hansson 	 * Using the deepest state for the CPU to trigger a potential selection
1947fbee48eSUlf Hansson 	 * of a shared state for the domain, assumes the domain states are all
1957fbee48eSUlf Hansson 	 * deeper states.
1967fbee48eSUlf Hansson 	 */
1977fbee48eSUlf Hansson 	drv->states[state_count - 1].enter = psci_enter_domain_idle_state;
198*670c90deSUlf Hansson 	drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state;
1997fbee48eSUlf Hansson 	psci_cpuidle_use_cpuhp = true;
2007fbee48eSUlf Hansson 
2017fbee48eSUlf Hansson 	return 0;
2027fbee48eSUlf Hansson }
2037fbee48eSUlf Hansson 
204166bf835SUlf Hansson static int psci_dt_cpu_init_idle(struct device *dev, struct cpuidle_driver *drv,
205a0cf3194SUlf Hansson 				 struct device_node *cpu_node,
2061595e4b0SUlf Hansson 				 unsigned int state_count, int cpu)
2079ffeb6d0SLorenzo Pieralisi {
2081595e4b0SUlf Hansson 	int i, ret = 0;
2099ffeb6d0SLorenzo Pieralisi 	u32 *psci_states;
2109ffeb6d0SLorenzo Pieralisi 	struct device_node *state_node;
2118554951aSUlf Hansson 	struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
2129ffeb6d0SLorenzo Pieralisi 
2131595e4b0SUlf Hansson 	state_count++; /* Add WFI state too */
214166bf835SUlf Hansson 	psci_states = devm_kcalloc(dev, state_count, sizeof(*psci_states),
215166bf835SUlf Hansson 				   GFP_KERNEL);
2169ffeb6d0SLorenzo Pieralisi 	if (!psci_states)
2179ffeb6d0SLorenzo Pieralisi 		return -ENOMEM;
2189ffeb6d0SLorenzo Pieralisi 
2191595e4b0SUlf Hansson 	for (i = 1; i < state_count; i++) {
220f08cfbfaSUlf Hansson 		state_node = of_get_cpu_state_node(cpu_node, i - 1);
2211595e4b0SUlf Hansson 		if (!state_node)
2221595e4b0SUlf Hansson 			break;
2231595e4b0SUlf Hansson 
2249ffeb6d0SLorenzo Pieralisi 		ret = psci_dt_parse_state_node(state_node, &psci_states[i]);
2259ffeb6d0SLorenzo Pieralisi 		of_node_put(state_node);
2269ffeb6d0SLorenzo Pieralisi 
2279ffeb6d0SLorenzo Pieralisi 		if (ret)
228166bf835SUlf Hansson 			return ret;
2299ffeb6d0SLorenzo Pieralisi 
2309ffeb6d0SLorenzo Pieralisi 		pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
2319ffeb6d0SLorenzo Pieralisi 	}
2329ffeb6d0SLorenzo Pieralisi 
233166bf835SUlf Hansson 	if (i != state_count)
234166bf835SUlf Hansson 		return -ENODEV;
2351595e4b0SUlf Hansson 
2367fbee48eSUlf Hansson 	/* Initialize optional data, used for the hierarchical topology. */
2377fbee48eSUlf Hansson 	ret = psci_dt_cpu_init_topology(drv, data, state_count, cpu);
2387fbee48eSUlf Hansson 	if (ret < 0)
239166bf835SUlf Hansson 		return ret;
2408554951aSUlf Hansson 
2418554951aSUlf Hansson 	/* Idle states parsed correctly, store them in the per-cpu struct. */
2428554951aSUlf Hansson 	data->psci_states = psci_states;
2439ffeb6d0SLorenzo Pieralisi 	return 0;
2449ffeb6d0SLorenzo Pieralisi }
2459ffeb6d0SLorenzo Pieralisi 
246166bf835SUlf Hansson static int psci_cpu_init_idle(struct device *dev, struct cpuidle_driver *drv,
247a0cf3194SUlf Hansson 			      unsigned int cpu, unsigned int state_count)
2489ffeb6d0SLorenzo Pieralisi {
2499ffeb6d0SLorenzo Pieralisi 	struct device_node *cpu_node;
2509ffeb6d0SLorenzo Pieralisi 	int ret;
2519ffeb6d0SLorenzo Pieralisi 
2529ffeb6d0SLorenzo Pieralisi 	/*
2539ffeb6d0SLorenzo Pieralisi 	 * If the PSCI cpu_suspend function hook has not been initialized
2549ffeb6d0SLorenzo Pieralisi 	 * idle states must not be enabled, so bail out
2559ffeb6d0SLorenzo Pieralisi 	 */
2569ffeb6d0SLorenzo Pieralisi 	if (!psci_ops.cpu_suspend)
2579ffeb6d0SLorenzo Pieralisi 		return -EOPNOTSUPP;
2589ffeb6d0SLorenzo Pieralisi 
2599ffeb6d0SLorenzo Pieralisi 	cpu_node = of_cpu_device_node_get(cpu);
2609ffeb6d0SLorenzo Pieralisi 	if (!cpu_node)
2619ffeb6d0SLorenzo Pieralisi 		return -ENODEV;
2629ffeb6d0SLorenzo Pieralisi 
263166bf835SUlf Hansson 	ret = psci_dt_cpu_init_idle(dev, drv, cpu_node, state_count, cpu);
2649ffeb6d0SLorenzo Pieralisi 
2659ffeb6d0SLorenzo Pieralisi 	of_node_put(cpu_node);
2669ffeb6d0SLorenzo Pieralisi 
2679ffeb6d0SLorenzo Pieralisi 	return ret;
2689ffeb6d0SLorenzo Pieralisi }
2699ffeb6d0SLorenzo Pieralisi 
270166bf835SUlf Hansson static void psci_cpu_deinit_idle(int cpu)
271166bf835SUlf Hansson {
272166bf835SUlf Hansson 	struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
273166bf835SUlf Hansson 
274166bf835SUlf Hansson 	psci_dt_detach_cpu(data->dev);
275166bf835SUlf Hansson 	psci_cpuidle_use_cpuhp = false;
276166bf835SUlf Hansson }
277166bf835SUlf Hansson 
278166bf835SUlf Hansson static int psci_idle_init_cpu(struct device *dev, int cpu)
27981d549e0SLorenzo Pieralisi {
28081d549e0SLorenzo Pieralisi 	struct cpuidle_driver *drv;
28181d549e0SLorenzo Pieralisi 	struct device_node *cpu_node;
28281d549e0SLorenzo Pieralisi 	const char *enable_method;
28381d549e0SLorenzo Pieralisi 	int ret = 0;
28481d549e0SLorenzo Pieralisi 
28581d549e0SLorenzo Pieralisi 	cpu_node = of_cpu_device_node_get(cpu);
28681d549e0SLorenzo Pieralisi 	if (!cpu_node)
28781d549e0SLorenzo Pieralisi 		return -ENODEV;
28881d549e0SLorenzo Pieralisi 
28981d549e0SLorenzo Pieralisi 	/*
29081d549e0SLorenzo Pieralisi 	 * Check whether the enable-method for the cpu is PSCI, fail
29181d549e0SLorenzo Pieralisi 	 * if it is not.
29281d549e0SLorenzo Pieralisi 	 */
29381d549e0SLorenzo Pieralisi 	enable_method = of_get_property(cpu_node, "enable-method", NULL);
29481d549e0SLorenzo Pieralisi 	if (!enable_method || (strcmp(enable_method, "psci")))
29581d549e0SLorenzo Pieralisi 		ret = -ENODEV;
29681d549e0SLorenzo Pieralisi 
29781d549e0SLorenzo Pieralisi 	of_node_put(cpu_node);
29881d549e0SLorenzo Pieralisi 	if (ret)
29981d549e0SLorenzo Pieralisi 		return ret;
30081d549e0SLorenzo Pieralisi 
301166bf835SUlf Hansson 	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
30281d549e0SLorenzo Pieralisi 	if (!drv)
30381d549e0SLorenzo Pieralisi 		return -ENOMEM;
30481d549e0SLorenzo Pieralisi 
305166bf835SUlf Hansson 	drv->name = "psci_idle";
306166bf835SUlf Hansson 	drv->owner = THIS_MODULE;
30781d549e0SLorenzo Pieralisi 	drv->cpumask = (struct cpumask *)cpumask_of(cpu);
30881d549e0SLorenzo Pieralisi 
30981d549e0SLorenzo Pieralisi 	/*
310166bf835SUlf Hansson 	 * PSCI idle states relies on architectural WFI to be represented as
311166bf835SUlf Hansson 	 * state index 0.
312166bf835SUlf Hansson 	 */
313166bf835SUlf Hansson 	drv->states[0].enter = psci_enter_idle_state;
314166bf835SUlf Hansson 	drv->states[0].exit_latency = 1;
315166bf835SUlf Hansson 	drv->states[0].target_residency = 1;
316166bf835SUlf Hansson 	drv->states[0].power_usage = UINT_MAX;
317166bf835SUlf Hansson 	strcpy(drv->states[0].name, "WFI");
318166bf835SUlf Hansson 	strcpy(drv->states[0].desc, "ARM WFI");
319166bf835SUlf Hansson 
320166bf835SUlf Hansson 	/*
32181d549e0SLorenzo Pieralisi 	 * If no DT idle states are detected (ret == 0) let the driver
32281d549e0SLorenzo Pieralisi 	 * initialization fail accordingly since there is no reason to
32381d549e0SLorenzo Pieralisi 	 * initialize the idle driver if only wfi is supported, the
32481d549e0SLorenzo Pieralisi 	 * default archictectural back-end already executes wfi
32581d549e0SLorenzo Pieralisi 	 * on idle entry.
32681d549e0SLorenzo Pieralisi 	 */
32781d549e0SLorenzo Pieralisi 	ret = dt_init_idle_driver(drv, psci_idle_state_match, 1);
328166bf835SUlf Hansson 	if (ret <= 0)
329166bf835SUlf Hansson 		return ret ? : -ENODEV;
33081d549e0SLorenzo Pieralisi 
33181d549e0SLorenzo Pieralisi 	/*
33281d549e0SLorenzo Pieralisi 	 * Initialize PSCI idle states.
33381d549e0SLorenzo Pieralisi 	 */
334166bf835SUlf Hansson 	ret = psci_cpu_init_idle(dev, drv, cpu, ret);
33581d549e0SLorenzo Pieralisi 	if (ret) {
33681d549e0SLorenzo Pieralisi 		pr_err("CPU %d failed to PSCI idle\n", cpu);
337166bf835SUlf Hansson 		return ret;
33881d549e0SLorenzo Pieralisi 	}
33981d549e0SLorenzo Pieralisi 
34081d549e0SLorenzo Pieralisi 	ret = cpuidle_register(drv, NULL);
34181d549e0SLorenzo Pieralisi 	if (ret)
342166bf835SUlf Hansson 		goto deinit;
34381d549e0SLorenzo Pieralisi 
344fc7a3d9eSDaniel Lezcano 	cpuidle_cooling_register(drv);
345fc7a3d9eSDaniel Lezcano 
34681d549e0SLorenzo Pieralisi 	return 0;
347166bf835SUlf Hansson deinit:
348166bf835SUlf Hansson 	psci_cpu_deinit_idle(cpu);
34981d549e0SLorenzo Pieralisi 	return ret;
35081d549e0SLorenzo Pieralisi }
35181d549e0SLorenzo Pieralisi 
35281d549e0SLorenzo Pieralisi /*
353166bf835SUlf Hansson  * psci_idle_probe - Initializes PSCI cpuidle driver
35481d549e0SLorenzo Pieralisi  *
35581d549e0SLorenzo Pieralisi  * Initializes PSCI cpuidle driver for all CPUs, if any CPU fails
35681d549e0SLorenzo Pieralisi  * to register cpuidle driver then rollback to cancel all CPUs
35781d549e0SLorenzo Pieralisi  * registration.
35881d549e0SLorenzo Pieralisi  */
359166bf835SUlf Hansson static int psci_cpuidle_probe(struct platform_device *pdev)
36081d549e0SLorenzo Pieralisi {
36181d549e0SLorenzo Pieralisi 	int cpu, ret;
36281d549e0SLorenzo Pieralisi 	struct cpuidle_driver *drv;
36381d549e0SLorenzo Pieralisi 	struct cpuidle_device *dev;
36481d549e0SLorenzo Pieralisi 
36581d549e0SLorenzo Pieralisi 	for_each_possible_cpu(cpu) {
366166bf835SUlf Hansson 		ret = psci_idle_init_cpu(&pdev->dev, cpu);
36781d549e0SLorenzo Pieralisi 		if (ret)
36881d549e0SLorenzo Pieralisi 			goto out_fail;
36981d549e0SLorenzo Pieralisi 	}
37081d549e0SLorenzo Pieralisi 
3719c6ceecbSUlf Hansson 	psci_idle_init_cpuhp();
37281d549e0SLorenzo Pieralisi 	return 0;
37381d549e0SLorenzo Pieralisi 
37481d549e0SLorenzo Pieralisi out_fail:
37581d549e0SLorenzo Pieralisi 	while (--cpu >= 0) {
37681d549e0SLorenzo Pieralisi 		dev = per_cpu(cpuidle_devices, cpu);
37781d549e0SLorenzo Pieralisi 		drv = cpuidle_get_cpu_driver(dev);
37881d549e0SLorenzo Pieralisi 		cpuidle_unregister(drv);
379166bf835SUlf Hansson 		psci_cpu_deinit_idle(cpu);
38081d549e0SLorenzo Pieralisi 	}
38181d549e0SLorenzo Pieralisi 
38281d549e0SLorenzo Pieralisi 	return ret;
38381d549e0SLorenzo Pieralisi }
384166bf835SUlf Hansson 
385166bf835SUlf Hansson static struct platform_driver psci_cpuidle_driver = {
386166bf835SUlf Hansson 	.probe = psci_cpuidle_probe,
387166bf835SUlf Hansson 	.driver = {
388166bf835SUlf Hansson 		.name = "psci-cpuidle",
389166bf835SUlf Hansson 	},
390166bf835SUlf Hansson };
391166bf835SUlf Hansson 
392166bf835SUlf Hansson static int __init psci_idle_init(void)
393166bf835SUlf Hansson {
394166bf835SUlf Hansson 	struct platform_device *pdev;
395166bf835SUlf Hansson 	int ret;
396166bf835SUlf Hansson 
397166bf835SUlf Hansson 	ret = platform_driver_register(&psci_cpuidle_driver);
398166bf835SUlf Hansson 	if (ret)
399166bf835SUlf Hansson 		return ret;
400166bf835SUlf Hansson 
401166bf835SUlf Hansson 	pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0);
402166bf835SUlf Hansson 	if (IS_ERR(pdev)) {
403166bf835SUlf Hansson 		platform_driver_unregister(&psci_cpuidle_driver);
404166bf835SUlf Hansson 		return PTR_ERR(pdev);
405166bf835SUlf Hansson 	}
406166bf835SUlf Hansson 
407166bf835SUlf Hansson 	return 0;
408166bf835SUlf Hansson }
40981d549e0SLorenzo Pieralisi device_initcall(psci_idle_init);
410