12c2e6ecfSDeepthi Dharwar /*
22c2e6ecfSDeepthi Dharwar  *  cpuidle-powernv - idle state cpuidle driver.
32c2e6ecfSDeepthi Dharwar  *  Adapted from drivers/cpuidle/cpuidle-pseries
42c2e6ecfSDeepthi Dharwar  *
52c2e6ecfSDeepthi Dharwar  */
62c2e6ecfSDeepthi Dharwar 
72c2e6ecfSDeepthi Dharwar #include <linux/kernel.h>
82c2e6ecfSDeepthi Dharwar #include <linux/module.h>
92c2e6ecfSDeepthi Dharwar #include <linux/init.h>
102c2e6ecfSDeepthi Dharwar #include <linux/moduleparam.h>
112c2e6ecfSDeepthi Dharwar #include <linux/cpuidle.h>
122c2e6ecfSDeepthi Dharwar #include <linux/cpu.h>
132c2e6ecfSDeepthi Dharwar #include <linux/notifier.h>
140d948730SPreeti U Murthy #include <linux/clockchips.h>
150888839cSPreeti U Murthy #include <linux/of.h>
1692c83ff5SPreeti U Murthy #include <linux/slab.h>
172c2e6ecfSDeepthi Dharwar 
182c2e6ecfSDeepthi Dharwar #include <asm/machdep.h>
192c2e6ecfSDeepthi Dharwar #include <asm/firmware.h>
208eb8ac89SShreyas B. Prabhu #include <asm/opal.h>
21591ac0cbSNicolas Pitre #include <asm/runlatch.h>
2209206b60SGautham R. Shenoy #include <asm/cpuidle.h>
232c2e6ecfSDeepthi Dharwar 
249e9fc6f0SGautham R. Shenoy /*
259e9fc6f0SGautham R. Shenoy  * Expose only those Hardware idle states via the cpuidle framework
269e9fc6f0SGautham R. Shenoy  * that have latency value below POWERNV_THRESHOLD_LATENCY_NS.
279e9fc6f0SGautham R. Shenoy  */
283005c597SShreyas B. Prabhu #define POWERNV_THRESHOLD_LATENCY_NS 200000
293005c597SShreyas B. Prabhu 
30ed61390bSAndrew Donnellan static struct cpuidle_driver powernv_idle_driver = {
312c2e6ecfSDeepthi Dharwar 	.name             = "powernv_idle",
322c2e6ecfSDeepthi Dharwar 	.owner            = THIS_MODULE,
332c2e6ecfSDeepthi Dharwar };
342c2e6ecfSDeepthi Dharwar 
35624e46d0SNicholas Piggin static int max_idle_state __read_mostly;
36624e46d0SNicholas Piggin static struct cpuidle_state *cpuidle_state_table __read_mostly;
373005c597SShreyas B. Prabhu 
3809206b60SGautham R. Shenoy struct stop_psscr_table {
3909206b60SGautham R. Shenoy 	u64 val;
4009206b60SGautham R. Shenoy 	u64 mask;
4109206b60SGautham R. Shenoy };
4209206b60SGautham R. Shenoy 
43624e46d0SNicholas Piggin static struct stop_psscr_table stop_psscr_table[CPUIDLE_STATE_MAX] __read_mostly;
443005c597SShreyas B. Prabhu 
45624e46d0SNicholas Piggin static u64 snooze_timeout __read_mostly;
46624e46d0SNicholas Piggin static bool snooze_timeout_en __read_mostly;
472c2e6ecfSDeepthi Dharwar 
482c2e6ecfSDeepthi Dharwar static int snooze_loop(struct cpuidle_device *dev,
492c2e6ecfSDeepthi Dharwar 			struct cpuidle_driver *drv,
502c2e6ecfSDeepthi Dharwar 			int index)
512c2e6ecfSDeepthi Dharwar {
5278eaa10fSShilpasri G Bhat 	u64 snooze_exit_time;
5378eaa10fSShilpasri G Bhat 
542c2e6ecfSDeepthi Dharwar 	set_thread_flag(TIF_POLLING_NRFLAG);
552c2e6ecfSDeepthi Dharwar 
563fc5ee92SNicholas Piggin 	local_irq_enable();
573fc5ee92SNicholas Piggin 
5878eaa10fSShilpasri G Bhat 	snooze_exit_time = get_tb() + snooze_timeout;
59591ac0cbSNicolas Pitre 	ppc64_runlatch_off();
602c2e6ecfSDeepthi Dharwar 	HMT_very_low();
6126eb48a9SAnton Blanchard 	while (!need_resched()) {
620baa91cbSAnton Blanchard 		if (likely(snooze_timeout_en) && get_tb() > snooze_exit_time)
6378eaa10fSShilpasri G Bhat 			break;
642c2e6ecfSDeepthi Dharwar 	}
652c2e6ecfSDeepthi Dharwar 
662c2e6ecfSDeepthi Dharwar 	HMT_medium();
67591ac0cbSNicolas Pitre 	ppc64_runlatch_on();
682c2e6ecfSDeepthi Dharwar 	clear_thread_flag(TIF_POLLING_NRFLAG);
692c2e6ecfSDeepthi Dharwar 	smp_mb();
703fc5ee92SNicholas Piggin 
712c2e6ecfSDeepthi Dharwar 	return index;
722c2e6ecfSDeepthi Dharwar }
732c2e6ecfSDeepthi Dharwar 
742c2e6ecfSDeepthi Dharwar static int nap_loop(struct cpuidle_device *dev,
752c2e6ecfSDeepthi Dharwar 			struct cpuidle_driver *drv,
762c2e6ecfSDeepthi Dharwar 			int index)
772c2e6ecfSDeepthi Dharwar {
782201f994SNicholas Piggin 	power7_idle_type(PNV_THREAD_NAP);
792201f994SNicholas Piggin 
802c2e6ecfSDeepthi Dharwar 	return index;
812c2e6ecfSDeepthi Dharwar }
822c2e6ecfSDeepthi Dharwar 
83cc5a2f7bSpreeti /* Register for fastsleep only in oneshot mode of broadcast */
84cc5a2f7bSpreeti #ifdef CONFIG_TICK_ONESHOT
850d948730SPreeti U Murthy static int fastsleep_loop(struct cpuidle_device *dev,
860d948730SPreeti U Murthy 				struct cpuidle_driver *drv,
870d948730SPreeti U Murthy 				int index)
880d948730SPreeti U Murthy {
890d948730SPreeti U Murthy 	unsigned long old_lpcr = mfspr(SPRN_LPCR);
900d948730SPreeti U Murthy 	unsigned long new_lpcr;
910d948730SPreeti U Murthy 
920d948730SPreeti U Murthy 	if (unlikely(system_state < SYSTEM_RUNNING))
930d948730SPreeti U Murthy 		return index;
940d948730SPreeti U Murthy 
950d948730SPreeti U Murthy 	new_lpcr = old_lpcr;
969b6a68d9SMichael Neuling 	/* Do not exit powersave upon decrementer as we've setup the timer
979b6a68d9SMichael Neuling 	 * offload.
980d948730SPreeti U Murthy 	 */
999b6a68d9SMichael Neuling 	new_lpcr &= ~LPCR_PECE1;
1000d948730SPreeti U Murthy 
1010d948730SPreeti U Murthy 	mtspr(SPRN_LPCR, new_lpcr);
1022201f994SNicholas Piggin 
1032201f994SNicholas Piggin 	power7_idle_type(PNV_THREAD_SLEEP);
1040d948730SPreeti U Murthy 
1050d948730SPreeti U Murthy 	mtspr(SPRN_LPCR, old_lpcr);
1060d948730SPreeti U Murthy 
1070d948730SPreeti U Murthy 	return index;
1080d948730SPreeti U Murthy }
109cc5a2f7bSpreeti #endif
1103005c597SShreyas B. Prabhu 
1113005c597SShreyas B. Prabhu static int stop_loop(struct cpuidle_device *dev,
1123005c597SShreyas B. Prabhu 		     struct cpuidle_driver *drv,
1133005c597SShreyas B. Prabhu 		     int index)
1143005c597SShreyas B. Prabhu {
1152201f994SNicholas Piggin 	power9_idle_type(stop_psscr_table[index].val,
11609206b60SGautham R. Shenoy 			 stop_psscr_table[index].mask);
1173005c597SShreyas B. Prabhu 	return index;
1183005c597SShreyas B. Prabhu }
1193005c597SShreyas B. Prabhu 
1202c2e6ecfSDeepthi Dharwar /*
1212c2e6ecfSDeepthi Dharwar  * States for dedicated partition case.
1222c2e6ecfSDeepthi Dharwar  */
123169f3faeSShreyas B. Prabhu static struct cpuidle_state powernv_states[CPUIDLE_STATE_MAX] = {
1242c2e6ecfSDeepthi Dharwar 	{ /* Snooze */
1252c2e6ecfSDeepthi Dharwar 		.name = "snooze",
1262c2e6ecfSDeepthi Dharwar 		.desc = "snooze",
1272c2e6ecfSDeepthi Dharwar 		.exit_latency = 0,
1282c2e6ecfSDeepthi Dharwar 		.target_residency = 0,
129957efcedSShreyas B. Prabhu 		.enter = snooze_loop },
1302c2e6ecfSDeepthi Dharwar };
1312c2e6ecfSDeepthi Dharwar 
13210fcca9dSSebastian Andrzej Siewior static int powernv_cpuidle_cpu_online(unsigned int cpu)
1332c2e6ecfSDeepthi Dharwar {
13410fcca9dSSebastian Andrzej Siewior 	struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
1352c2e6ecfSDeepthi Dharwar 
1362c2e6ecfSDeepthi Dharwar 	if (dev && cpuidle_get_driver()) {
1372c2e6ecfSDeepthi Dharwar 		cpuidle_pause_and_lock();
1382c2e6ecfSDeepthi Dharwar 		cpuidle_enable_device(dev);
1392c2e6ecfSDeepthi Dharwar 		cpuidle_resume_and_unlock();
14010fcca9dSSebastian Andrzej Siewior 	}
14110fcca9dSSebastian Andrzej Siewior 	return 0;
14210fcca9dSSebastian Andrzej Siewior }
1432c2e6ecfSDeepthi Dharwar 
14410fcca9dSSebastian Andrzej Siewior static int powernv_cpuidle_cpu_dead(unsigned int cpu)
14510fcca9dSSebastian Andrzej Siewior {
14610fcca9dSSebastian Andrzej Siewior 	struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
14710fcca9dSSebastian Andrzej Siewior 
14810fcca9dSSebastian Andrzej Siewior 	if (dev && cpuidle_get_driver()) {
1492c2e6ecfSDeepthi Dharwar 		cpuidle_pause_and_lock();
1502c2e6ecfSDeepthi Dharwar 		cpuidle_disable_device(dev);
1512c2e6ecfSDeepthi Dharwar 		cpuidle_resume_and_unlock();
1522c2e6ecfSDeepthi Dharwar 	}
15310fcca9dSSebastian Andrzej Siewior 	return 0;
1542c2e6ecfSDeepthi Dharwar }
1552c2e6ecfSDeepthi Dharwar 
1562c2e6ecfSDeepthi Dharwar /*
1572c2e6ecfSDeepthi Dharwar  * powernv_cpuidle_driver_init()
1582c2e6ecfSDeepthi Dharwar  */
1592c2e6ecfSDeepthi Dharwar static int powernv_cpuidle_driver_init(void)
1602c2e6ecfSDeepthi Dharwar {
1612c2e6ecfSDeepthi Dharwar 	int idle_state;
1622c2e6ecfSDeepthi Dharwar 	struct cpuidle_driver *drv = &powernv_idle_driver;
1632c2e6ecfSDeepthi Dharwar 
1642c2e6ecfSDeepthi Dharwar 	drv->state_count = 0;
1652c2e6ecfSDeepthi Dharwar 
1662c2e6ecfSDeepthi Dharwar 	for (idle_state = 0; idle_state < max_idle_state; ++idle_state) {
1672c2e6ecfSDeepthi Dharwar 		/* Is the state not enabled? */
1682c2e6ecfSDeepthi Dharwar 		if (cpuidle_state_table[idle_state].enter == NULL)
1692c2e6ecfSDeepthi Dharwar 			continue;
1702c2e6ecfSDeepthi Dharwar 
1712c2e6ecfSDeepthi Dharwar 		drv->states[drv->state_count] =	/* structure copy */
1722c2e6ecfSDeepthi Dharwar 			cpuidle_state_table[idle_state];
1732c2e6ecfSDeepthi Dharwar 
1742c2e6ecfSDeepthi Dharwar 		drv->state_count += 1;
1752c2e6ecfSDeepthi Dharwar 	}
1762c2e6ecfSDeepthi Dharwar 
177293d264fSVaidyanathan Srinivasan 	/*
178293d264fSVaidyanathan Srinivasan 	 * On the PowerNV platform cpu_present may be less than cpu_possible in
179293d264fSVaidyanathan Srinivasan 	 * cases when firmware detects the CPU, but it is not available to the
180293d264fSVaidyanathan Srinivasan 	 * OS.  If CONFIG_HOTPLUG_CPU=n, then such CPUs are not hotplugable at
181293d264fSVaidyanathan Srinivasan 	 * run time and hence cpu_devices are not created for those CPUs by the
182293d264fSVaidyanathan Srinivasan 	 * generic topology_init().
183293d264fSVaidyanathan Srinivasan 	 *
184293d264fSVaidyanathan Srinivasan 	 * drv->cpumask defaults to cpu_possible_mask in
185293d264fSVaidyanathan Srinivasan 	 * __cpuidle_driver_init().  This breaks cpuidle on PowerNV where
186293d264fSVaidyanathan Srinivasan 	 * cpu_devices are not created for CPUs in cpu_possible_mask that
187293d264fSVaidyanathan Srinivasan 	 * cannot be hot-added later at run time.
188293d264fSVaidyanathan Srinivasan 	 *
189293d264fSVaidyanathan Srinivasan 	 * Trying cpuidle_register_device() on a CPU without a cpu_device is
190293d264fSVaidyanathan Srinivasan 	 * incorrect, so pass a correct CPU mask to the generic cpuidle driver.
191293d264fSVaidyanathan Srinivasan 	 */
192293d264fSVaidyanathan Srinivasan 
193293d264fSVaidyanathan Srinivasan 	drv->cpumask = (struct cpumask *)cpu_present_mask;
194293d264fSVaidyanathan Srinivasan 
1952c2e6ecfSDeepthi Dharwar 	return 0;
1962c2e6ecfSDeepthi Dharwar }
1972c2e6ecfSDeepthi Dharwar 
1989e9fc6f0SGautham R. Shenoy static inline void add_powernv_state(int index, const char *name,
1999e9fc6f0SGautham R. Shenoy 				     unsigned int flags,
2009e9fc6f0SGautham R. Shenoy 				     int (*idle_fn)(struct cpuidle_device *,
2019e9fc6f0SGautham R. Shenoy 						    struct cpuidle_driver *,
2029e9fc6f0SGautham R. Shenoy 						    int),
2039e9fc6f0SGautham R. Shenoy 				     unsigned int target_residency,
2049e9fc6f0SGautham R. Shenoy 				     unsigned int exit_latency,
20509206b60SGautham R. Shenoy 				     u64 psscr_val, u64 psscr_mask)
2069e9fc6f0SGautham R. Shenoy {
2079e9fc6f0SGautham R. Shenoy 	strlcpy(powernv_states[index].name, name, CPUIDLE_NAME_LEN);
2089e9fc6f0SGautham R. Shenoy 	strlcpy(powernv_states[index].desc, name, CPUIDLE_NAME_LEN);
2099e9fc6f0SGautham R. Shenoy 	powernv_states[index].flags = flags;
2109e9fc6f0SGautham R. Shenoy 	powernv_states[index].target_residency = target_residency;
2119e9fc6f0SGautham R. Shenoy 	powernv_states[index].exit_latency = exit_latency;
2129e9fc6f0SGautham R. Shenoy 	powernv_states[index].enter = idle_fn;
21309206b60SGautham R. Shenoy 	stop_psscr_table[index].val = psscr_val;
21409206b60SGautham R. Shenoy 	stop_psscr_table[index].mask = psscr_mask;
2159e9fc6f0SGautham R. Shenoy }
2169e9fc6f0SGautham R. Shenoy 
217ecad4502SGautham R. Shenoy /*
218ecad4502SGautham R. Shenoy  * Returns 0 if prop1_len == prop2_len. Else returns -1
219ecad4502SGautham R. Shenoy  */
220ecad4502SGautham R. Shenoy static inline int validate_dt_prop_sizes(const char *prop1, int prop1_len,
221ecad4502SGautham R. Shenoy 					 const char *prop2, int prop2_len)
222ecad4502SGautham R. Shenoy {
223ecad4502SGautham R. Shenoy 	if (prop1_len == prop2_len)
224ecad4502SGautham R. Shenoy 		return 0;
225ecad4502SGautham R. Shenoy 
226ecad4502SGautham R. Shenoy 	pr_warn("cpuidle-powernv: array sizes don't match for %s and %s\n",
227ecad4502SGautham R. Shenoy 		prop1, prop2);
228ecad4502SGautham R. Shenoy 	return -1;
229ecad4502SGautham R. Shenoy }
230ecad4502SGautham R. Shenoy 
2310888839cSPreeti U Murthy static int powernv_add_idle_states(void)
2320888839cSPreeti U Murthy {
2330888839cSPreeti U Murthy 	struct device_node *power_mgt;
2340888839cSPreeti U Murthy 	int nr_idle_states = 1; /* Snooze */
235ecad4502SGautham R. Shenoy 	int dt_idle_states, count;
236957efcedSShreyas B. Prabhu 	u32 latency_ns[CPUIDLE_STATE_MAX];
237957efcedSShreyas B. Prabhu 	u32 residency_ns[CPUIDLE_STATE_MAX];
238957efcedSShreyas B. Prabhu 	u32 flags[CPUIDLE_STATE_MAX];
2393005c597SShreyas B. Prabhu 	u64 psscr_val[CPUIDLE_STATE_MAX];
24009206b60SGautham R. Shenoy 	u64 psscr_mask[CPUIDLE_STATE_MAX];
2413005c597SShreyas B. Prabhu 	const char *names[CPUIDLE_STATE_MAX];
24209206b60SGautham R. Shenoy 	u32 has_stop_states = 0;
24392c83ff5SPreeti U Murthy 	int i, rc;
2440888839cSPreeti U Murthy 
2450888839cSPreeti U Murthy 	/* Currently we have snooze statically defined */
2460888839cSPreeti U Murthy 
2470888839cSPreeti U Murthy 	power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
2480888839cSPreeti U Murthy 	if (!power_mgt) {
2490888839cSPreeti U Murthy 		pr_warn("opal: PowerMgmt Node not found\n");
25092c83ff5SPreeti U Murthy 		goto out;
2510888839cSPreeti U Murthy 	}
2520888839cSPreeti U Murthy 
25370734a78SPreeti U Murthy 	/* Read values of any property to determine the num of idle states */
25470734a78SPreeti U Murthy 	dt_idle_states = of_property_count_u32_elems(power_mgt, "ibm,cpu-idle-state-flags");
25570734a78SPreeti U Murthy 	if (dt_idle_states < 0) {
25670734a78SPreeti U Murthy 		pr_warn("cpuidle-powernv: no idle states found in the DT\n");
25792c83ff5SPreeti U Murthy 		goto out;
25874aa51b5SPreeti U. Murthy 	}
25974aa51b5SPreeti U. Murthy 
260ecad4502SGautham R. Shenoy 	count = of_property_count_u32_elems(power_mgt,
261ecad4502SGautham R. Shenoy 					    "ibm,cpu-idle-state-latencies-ns");
262ecad4502SGautham R. Shenoy 
263ecad4502SGautham R. Shenoy 	if (validate_dt_prop_sizes("ibm,cpu-idle-state-flags", dt_idle_states,
264ecad4502SGautham R. Shenoy 				   "ibm,cpu-idle-state-latencies-ns",
265ecad4502SGautham R. Shenoy 				   count) != 0)
266ecad4502SGautham R. Shenoy 		goto out;
267ecad4502SGautham R. Shenoy 
268ecad4502SGautham R. Shenoy 	count = of_property_count_strings(power_mgt,
269ecad4502SGautham R. Shenoy 					  "ibm,cpu-idle-state-names");
270ecad4502SGautham R. Shenoy 	if (validate_dt_prop_sizes("ibm,cpu-idle-state-flags", dt_idle_states,
271ecad4502SGautham R. Shenoy 				   "ibm,cpu-idle-state-names",
272ecad4502SGautham R. Shenoy 				   count) != 0)
273ecad4502SGautham R. Shenoy 		goto out;
274ecad4502SGautham R. Shenoy 
275957efcedSShreyas B. Prabhu 	/*
276957efcedSShreyas B. Prabhu 	 * Since snooze is used as first idle state, max idle states allowed is
277957efcedSShreyas B. Prabhu 	 * CPUIDLE_STATE_MAX -1
278957efcedSShreyas B. Prabhu 	 */
279957efcedSShreyas B. Prabhu 	if (dt_idle_states > CPUIDLE_STATE_MAX - 1) {
280957efcedSShreyas B. Prabhu 		pr_warn("cpuidle-powernv: discovered idle states more than allowed");
281957efcedSShreyas B. Prabhu 		dt_idle_states = CPUIDLE_STATE_MAX - 1;
282957efcedSShreyas B. Prabhu 	}
283957efcedSShreyas B. Prabhu 
28470734a78SPreeti U Murthy 	if (of_property_read_u32_array(power_mgt,
28570734a78SPreeti U Murthy 			"ibm,cpu-idle-state-flags", flags, dt_idle_states)) {
28670734a78SPreeti U Murthy 		pr_warn("cpuidle-powernv : missing ibm,cpu-idle-state-flags in DT\n");
287957efcedSShreyas B. Prabhu 		goto out;
28870734a78SPreeti U Murthy 	}
2890888839cSPreeti U Murthy 
290957efcedSShreyas B. Prabhu 	if (of_property_read_u32_array(power_mgt,
291957efcedSShreyas B. Prabhu 		"ibm,cpu-idle-state-latencies-ns", latency_ns,
292957efcedSShreyas B. Prabhu 		dt_idle_states)) {
29392c83ff5SPreeti U Murthy 		pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-latencies-ns in DT\n");
294957efcedSShreyas B. Prabhu 		goto out;
29592c83ff5SPreeti U Murthy 	}
2963005c597SShreyas B. Prabhu 	if (of_property_read_string_array(power_mgt,
2973005c597SShreyas B. Prabhu 		"ibm,cpu-idle-state-names", names, dt_idle_states) < 0) {
2983005c597SShreyas B. Prabhu 		pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-names in DT\n");
2993005c597SShreyas B. Prabhu 		goto out;
3003005c597SShreyas B. Prabhu 	}
3013005c597SShreyas B. Prabhu 
3023005c597SShreyas B. Prabhu 	/*
3033005c597SShreyas B. Prabhu 	 * If the idle states use stop instruction, probe for psscr values
30409206b60SGautham R. Shenoy 	 * and psscr mask which are necessary to specify required stop level.
3053005c597SShreyas B. Prabhu 	 */
30609206b60SGautham R. Shenoy 	has_stop_states = (flags[0] &
30709206b60SGautham R. Shenoy 			   (OPAL_PM_STOP_INST_FAST | OPAL_PM_STOP_INST_DEEP));
30809206b60SGautham R. Shenoy 	if (has_stop_states) {
309ecad4502SGautham R. Shenoy 		count = of_property_count_u64_elems(power_mgt,
310ecad4502SGautham R. Shenoy 						    "ibm,cpu-idle-state-psscr");
311ecad4502SGautham R. Shenoy 		if (validate_dt_prop_sizes("ibm,cpu-idle-state-flags",
312ecad4502SGautham R. Shenoy 					   dt_idle_states,
313ecad4502SGautham R. Shenoy 					   "ibm,cpu-idle-state-psscr",
314ecad4502SGautham R. Shenoy 					   count) != 0)
315ecad4502SGautham R. Shenoy 			goto out;
316ecad4502SGautham R. Shenoy 
317ecad4502SGautham R. Shenoy 		count = of_property_count_u64_elems(power_mgt,
318ecad4502SGautham R. Shenoy 						    "ibm,cpu-idle-state-psscr-mask");
319ecad4502SGautham R. Shenoy 		if (validate_dt_prop_sizes("ibm,cpu-idle-state-flags",
320ecad4502SGautham R. Shenoy 					   dt_idle_states,
321ecad4502SGautham R. Shenoy 					   "ibm,cpu-idle-state-psscr-mask",
322ecad4502SGautham R. Shenoy 					   count) != 0)
323ecad4502SGautham R. Shenoy 			goto out;
324ecad4502SGautham R. Shenoy 
3253005c597SShreyas B. Prabhu 		if (of_property_read_u64_array(power_mgt,
3263005c597SShreyas B. Prabhu 		    "ibm,cpu-idle-state-psscr", psscr_val, dt_idle_states)) {
32709206b60SGautham R. Shenoy 			pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
3283005c597SShreyas B. Prabhu 			goto out;
3293005c597SShreyas B. Prabhu 		}
33092c83ff5SPreeti U Murthy 
33109206b60SGautham R. Shenoy 		if (of_property_read_u64_array(power_mgt,
33209206b60SGautham R. Shenoy 					       "ibm,cpu-idle-state-psscr-mask",
33309206b60SGautham R. Shenoy 						psscr_mask, dt_idle_states)) {
33409206b60SGautham R. Shenoy 			pr_warn("cpuidle-powernv:Missing ibm,cpu-idle-state-psscr-mask in DT\n");
33509206b60SGautham R. Shenoy 			goto out;
33609206b60SGautham R. Shenoy 		}
33709206b60SGautham R. Shenoy 	}
33809206b60SGautham R. Shenoy 
339ecad4502SGautham R. Shenoy 	count = of_property_count_u32_elems(power_mgt,
340ecad4502SGautham R. Shenoy 					    "ibm,cpu-idle-state-residency-ns");
341ecad4502SGautham R. Shenoy 
342ecad4502SGautham R. Shenoy 	if (count < 0) {
343ecad4502SGautham R. Shenoy 		rc = count;
344ecad4502SGautham R. Shenoy 	} else if (validate_dt_prop_sizes("ibm,cpu-idle-state-flags",
345ecad4502SGautham R. Shenoy 					  dt_idle_states,
346ecad4502SGautham R. Shenoy 					  "ibm,cpu-idle-state-residency-ns",
347ecad4502SGautham R. Shenoy 					  count) != 0) {
348ecad4502SGautham R. Shenoy 		goto out;
349ecad4502SGautham R. Shenoy 	} else {
35092c83ff5SPreeti U Murthy 		rc = of_property_read_u32_array(power_mgt,
351ecad4502SGautham R. Shenoy 						"ibm,cpu-idle-state-residency-ns",
352ecad4502SGautham R. Shenoy 						residency_ns, dt_idle_states);
353ecad4502SGautham R. Shenoy 	}
35492c83ff5SPreeti U Murthy 
3550888839cSPreeti U Murthy 	for (i = 0; i < dt_idle_states; i++) {
3569e9fc6f0SGautham R. Shenoy 		unsigned int exit_latency, target_residency;
357f9122ee4SGautham R. Shenoy 		bool stops_timebase = false;
3583005c597SShreyas B. Prabhu 		/*
3593005c597SShreyas B. Prabhu 		 * If an idle state has exit latency beyond
3603005c597SShreyas B. Prabhu 		 * POWERNV_THRESHOLD_LATENCY_NS then don't use it
3613005c597SShreyas B. Prabhu 		 * in cpu-idle.
3623005c597SShreyas B. Prabhu 		 */
3633005c597SShreyas B. Prabhu 		if (latency_ns[i] > POWERNV_THRESHOLD_LATENCY_NS)
3643005c597SShreyas B. Prabhu 			continue;
3659e9fc6f0SGautham R. Shenoy 		/*
3669e9fc6f0SGautham R. Shenoy 		 * Firmware passes residency and latency values in ns.
3679e9fc6f0SGautham R. Shenoy 		 * cpuidle expects it in us.
3689e9fc6f0SGautham R. Shenoy 		 */
3699e9fc6f0SGautham R. Shenoy 		exit_latency = latency_ns[i] / 1000;
3709e9fc6f0SGautham R. Shenoy 		if (!rc)
3719e9fc6f0SGautham R. Shenoy 			target_residency = residency_ns[i] / 1000;
3729e9fc6f0SGautham R. Shenoy 		else
3739e9fc6f0SGautham R. Shenoy 			target_residency = 0;
3740888839cSPreeti U Murthy 
37509206b60SGautham R. Shenoy 		if (has_stop_states) {
37609206b60SGautham R. Shenoy 			int err = validate_psscr_val_mask(&psscr_val[i],
37709206b60SGautham R. Shenoy 							  &psscr_mask[i],
37809206b60SGautham R. Shenoy 							  flags[i]);
37909206b60SGautham R. Shenoy 			if (err) {
38009206b60SGautham R. Shenoy 				report_invalid_psscr_val(psscr_val[i], err);
38109206b60SGautham R. Shenoy 				continue;
38209206b60SGautham R. Shenoy 			}
38309206b60SGautham R. Shenoy 		}
38409206b60SGautham R. Shenoy 
385f9122ee4SGautham R. Shenoy 		if (flags[i] & OPAL_PM_TIMEBASE_STOP)
386f9122ee4SGautham R. Shenoy 			stops_timebase = true;
387f9122ee4SGautham R. Shenoy 
38892c83ff5SPreeti U Murthy 		/*
3899e9fc6f0SGautham R. Shenoy 		 * For nap and fastsleep, use default target_residency
3909e9fc6f0SGautham R. Shenoy 		 * values if f/w does not expose it.
39174aa51b5SPreeti U. Murthy 		 */
39270734a78SPreeti U Murthy 		if (flags[i] & OPAL_PM_NAP_ENABLED) {
3939e9fc6f0SGautham R. Shenoy 			if (!rc)
3949e9fc6f0SGautham R. Shenoy 				target_residency = 100;
3950888839cSPreeti U Murthy 			/* Add NAP state */
3969e9fc6f0SGautham R. Shenoy 			add_powernv_state(nr_idle_states, "Nap",
3979e9fc6f0SGautham R. Shenoy 					  CPUIDLE_FLAG_NONE, nap_loop,
39809206b60SGautham R. Shenoy 					  target_residency, exit_latency, 0, 0);
399f9122ee4SGautham R. Shenoy 		} else if (has_stop_states && !stops_timebase) {
4009e9fc6f0SGautham R. Shenoy 			add_powernv_state(nr_idle_states, names[i],
4019e9fc6f0SGautham R. Shenoy 					  CPUIDLE_FLAG_NONE, stop_loop,
4029e9fc6f0SGautham R. Shenoy 					  target_residency, exit_latency,
40309206b60SGautham R. Shenoy 					  psscr_val[i], psscr_mask[i]);
404cc5a2f7bSpreeti 		}
405cc5a2f7bSpreeti 
406cc5a2f7bSpreeti 		/*
407cc5a2f7bSpreeti 		 * All cpuidle states with CPUIDLE_FLAG_TIMER_STOP set must come
408cc5a2f7bSpreeti 		 * within this config dependency check.
409cc5a2f7bSpreeti 		 */
410cc5a2f7bSpreeti #ifdef CONFIG_TICK_ONESHOT
411f9122ee4SGautham R. Shenoy 		else if (flags[i] & OPAL_PM_SLEEP_ENABLED ||
41270734a78SPreeti U Murthy 			 flags[i] & OPAL_PM_SLEEP_ENABLED_ER1) {
4139e9fc6f0SGautham R. Shenoy 			if (!rc)
4149e9fc6f0SGautham R. Shenoy 				target_residency = 300000;
4150888839cSPreeti U Murthy 			/* Add FASTSLEEP state */
4169e9fc6f0SGautham R. Shenoy 			add_powernv_state(nr_idle_states, "FastSleep",
4179e9fc6f0SGautham R. Shenoy 					  CPUIDLE_FLAG_TIMER_STOP,
4189e9fc6f0SGautham R. Shenoy 					  fastsleep_loop,
41909206b60SGautham R. Shenoy 					  target_residency, exit_latency, 0, 0);
420f9122ee4SGautham R. Shenoy 		} else if (has_stop_states && stops_timebase) {
4219e9fc6f0SGautham R. Shenoy 			add_powernv_state(nr_idle_states, names[i],
4229e9fc6f0SGautham R. Shenoy 					  CPUIDLE_FLAG_TIMER_STOP, stop_loop,
4239e9fc6f0SGautham R. Shenoy 					  target_residency, exit_latency,
42409206b60SGautham R. Shenoy 					  psscr_val[i], psscr_mask[i]);
4250888839cSPreeti U Murthy 		}
426cc5a2f7bSpreeti #endif
427f9122ee4SGautham R. Shenoy 		else
428f9122ee4SGautham R. Shenoy 			continue;
42992c83ff5SPreeti U Murthy 		nr_idle_states++;
43092c83ff5SPreeti U Murthy 	}
43192c83ff5SPreeti U Murthy out:
4320888839cSPreeti U Murthy 	return nr_idle_states;
4330888839cSPreeti U Murthy }
4340888839cSPreeti U Murthy 
4352c2e6ecfSDeepthi Dharwar /*
4362c2e6ecfSDeepthi Dharwar  * powernv_idle_probe()
4372c2e6ecfSDeepthi Dharwar  * Choose state table for shared versus dedicated partition
4382c2e6ecfSDeepthi Dharwar  */
4392c2e6ecfSDeepthi Dharwar static int powernv_idle_probe(void)
4402c2e6ecfSDeepthi Dharwar {
4412c2e6ecfSDeepthi Dharwar 	if (cpuidle_disable != IDLE_NO_OVERRIDE)
4422c2e6ecfSDeepthi Dharwar 		return -ENODEV;
4432c2e6ecfSDeepthi Dharwar 
444e4d54f71SStewart Smith 	if (firmware_has_feature(FW_FEATURE_OPAL)) {
4452c2e6ecfSDeepthi Dharwar 		cpuidle_state_table = powernv_states;
4460888839cSPreeti U Murthy 		/* Device tree can indicate more idle states */
4470888839cSPreeti U Murthy 		max_idle_state = powernv_add_idle_states();
44878eaa10fSShilpasri G Bhat 		if (max_idle_state > 1) {
44978eaa10fSShilpasri G Bhat 			snooze_timeout_en = true;
45078eaa10fSShilpasri G Bhat 			snooze_timeout = powernv_states[1].target_residency *
45178eaa10fSShilpasri G Bhat 					 tb_ticks_per_usec;
45278eaa10fSShilpasri G Bhat 		}
4532c2e6ecfSDeepthi Dharwar  	} else
4542c2e6ecfSDeepthi Dharwar  		return -ENODEV;
4552c2e6ecfSDeepthi Dharwar 
4562c2e6ecfSDeepthi Dharwar 	return 0;
4572c2e6ecfSDeepthi Dharwar }
4582c2e6ecfSDeepthi Dharwar 
4592c2e6ecfSDeepthi Dharwar static int __init powernv_processor_idle_init(void)
4602c2e6ecfSDeepthi Dharwar {
4612c2e6ecfSDeepthi Dharwar 	int retval;
4622c2e6ecfSDeepthi Dharwar 
4632c2e6ecfSDeepthi Dharwar 	retval = powernv_idle_probe();
4642c2e6ecfSDeepthi Dharwar 	if (retval)
4652c2e6ecfSDeepthi Dharwar 		return retval;
4662c2e6ecfSDeepthi Dharwar 
4672c2e6ecfSDeepthi Dharwar 	powernv_cpuidle_driver_init();
4682c2e6ecfSDeepthi Dharwar 	retval = cpuidle_register(&powernv_idle_driver, NULL);
4692c2e6ecfSDeepthi Dharwar 	if (retval) {
4702c2e6ecfSDeepthi Dharwar 		printk(KERN_DEBUG "Registration of powernv driver failed.\n");
4712c2e6ecfSDeepthi Dharwar 		return retval;
4722c2e6ecfSDeepthi Dharwar 	}
4732c2e6ecfSDeepthi Dharwar 
47410fcca9dSSebastian Andrzej Siewior 	retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
47510fcca9dSSebastian Andrzej Siewior 					   "cpuidle/powernv:online",
47610fcca9dSSebastian Andrzej Siewior 					   powernv_cpuidle_cpu_online, NULL);
47710fcca9dSSebastian Andrzej Siewior 	WARN_ON(retval < 0);
47810fcca9dSSebastian Andrzej Siewior 	retval = cpuhp_setup_state_nocalls(CPUHP_CPUIDLE_DEAD,
47910fcca9dSSebastian Andrzej Siewior 					   "cpuidle/powernv:dead", NULL,
48010fcca9dSSebastian Andrzej Siewior 					   powernv_cpuidle_cpu_dead);
48110fcca9dSSebastian Andrzej Siewior 	WARN_ON(retval < 0);
4822c2e6ecfSDeepthi Dharwar 	printk(KERN_DEBUG "powernv_idle_driver registered\n");
4832c2e6ecfSDeepthi Dharwar 	return 0;
4842c2e6ecfSDeepthi Dharwar }
4852c2e6ecfSDeepthi Dharwar 
4862c2e6ecfSDeepthi Dharwar device_initcall(powernv_processor_idle_init);
487