Commit eea3e2de authored by Will Deacon's avatar Will Deacon
Browse files

Merge branch 'for-next/cpuidle' into for-next/core

Fix resume from idle when pNMI is being used.

* for-next/cpuidle:
  arm64: suspend: Use cpuidle context helpers in cpu_suspend()
  PSCI: Use cpuidle context helpers in psci_cpu_suspend_enter()
  arm64: Convert cpu_do_idle() to using cpuidle context helpers
  arm64: Add cpuidle context save/restore helpers
parents eda2171d 77345ef7
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -49,4 +49,9 @@ extern int arm_cpuidle_suspend(int index);


extern int arm_cpuidle_init(int cpu);
extern int arm_cpuidle_init(int cpu);


struct arm_cpuidle_irq_context { };

#define arm_cpuidle_save_irq_context(c)		(void)c
#define arm_cpuidle_restore_irq_context(c)	(void)c

#endif
#endif
+35 −0
Original line number Original line Diff line number Diff line
@@ -18,4 +18,39 @@ static inline int arm_cpuidle_suspend(int index)
	return -EOPNOTSUPP;
	return -EOPNOTSUPP;
}
}
#endif
#endif

#ifdef CONFIG_ARM64_PSEUDO_NMI
#include <asm/arch_gicv3.h>

struct arm_cpuidle_irq_context {
	unsigned long pmr;
	unsigned long daif_bits;
};

#define arm_cpuidle_save_irq_context(__c)				\
	do {								\
		struct arm_cpuidle_irq_context *c = __c;		\
		if (system_uses_irq_prio_masking()) {			\
			c->daif_bits = read_sysreg(daif);		\
			write_sysreg(c->daif_bits | PSR_I_BIT | PSR_F_BIT, \
				     daif);				\
			c->pmr = gic_read_pmr();			\
			gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); \
		}							\
	} while (0)

#define arm_cpuidle_restore_irq_context(__c)				\
	do {								\
		struct arm_cpuidle_irq_context *c = __c;		\
		if (system_uses_irq_prio_masking()) {			\
			gic_write_pmr(c->pmr);				\
			write_sysreg(c->daif_bits, daif);		\
		}							\
	} while (0)
#else
struct arm_cpuidle_irq_context { };

#define arm_cpuidle_save_irq_context(c)		(void)c
#define arm_cpuidle_restore_irq_context(c)	(void)c
#endif
#endif
#endif
+9 −32
Original line number Original line Diff line number Diff line
@@ -46,9 +46,9 @@
#include <linux/prctl.h>
#include <linux/prctl.h>


#include <asm/alternative.h>
#include <asm/alternative.h>
#include <asm/arch_gicv3.h>
#include <asm/compat.h>
#include <asm/compat.h>
#include <asm/cpufeature.h>
#include <asm/cpufeature.h>
#include <asm/cpuidle.h>
#include <asm/cacheflush.h>
#include <asm/cacheflush.h>
#include <asm/exec.h>
#include <asm/exec.h>
#include <asm/fpsimd.h>
#include <asm/fpsimd.h>
@@ -74,33 +74,6 @@ EXPORT_SYMBOL_GPL(pm_power_off);


void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);


static void noinstr __cpu_do_idle(void)
{
	dsb(sy);
	wfi();
}

static void noinstr __cpu_do_idle_irqprio(void)
{
	unsigned long pmr;
	unsigned long daif_bits;

	daif_bits = read_sysreg(daif);
	write_sysreg(daif_bits | PSR_I_BIT | PSR_F_BIT, daif);

	/*
	 * Unmask PMR before going idle to make sure interrupts can
	 * be raised.
	 */
	pmr = gic_read_pmr();
	gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET);

	__cpu_do_idle();

	gic_write_pmr(pmr);
	write_sysreg(daif_bits, daif);
}

/*
/*
 *	cpu_do_idle()
 *	cpu_do_idle()
 *
 *
@@ -112,10 +85,14 @@ static void noinstr __cpu_do_idle_irqprio(void)
 */
 */
void noinstr cpu_do_idle(void)
void noinstr cpu_do_idle(void)
{
{
	if (system_uses_irq_prio_masking())
	struct arm_cpuidle_irq_context context;
		__cpu_do_idle_irqprio();

	else
	arm_cpuidle_save_irq_context(&context);
		__cpu_do_idle();

	dsb(sy);
	wfi();

	arm_cpuidle_restore_irq_context(&context);
}
}


/*
/*
+11 −1
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@
#include <asm/alternative.h>
#include <asm/alternative.h>
#include <asm/cacheflush.h>
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#include <asm/cpufeature.h>
#include <asm/cpuidle.h>
#include <asm/daifflags.h>
#include <asm/daifflags.h>
#include <asm/debug-monitors.h>
#include <asm/debug-monitors.h>
#include <asm/exec.h>
#include <asm/exec.h>
@@ -91,6 +92,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
	int ret = 0;
	int ret = 0;
	unsigned long flags;
	unsigned long flags;
	struct sleep_stack_data state;
	struct sleep_stack_data state;
	struct arm_cpuidle_irq_context context;


	/* Report any MTE async fault before going to suspend */
	/* Report any MTE async fault before going to suspend */
	mte_suspend_enter();
	mte_suspend_enter();
@@ -103,12 +105,18 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
	flags = local_daif_save();
	flags = local_daif_save();


	/*
	/*
	 * Function graph tracer state gets incosistent when the kernel
	 * Function graph tracer state gets inconsistent when the kernel
	 * calls functions that never return (aka suspend finishers) hence
	 * calls functions that never return (aka suspend finishers) hence
	 * disable graph tracing during their execution.
	 * disable graph tracing during their execution.
	 */
	 */
	pause_graph_tracing();
	pause_graph_tracing();


	/*
	 * Switch to using DAIF.IF instead of PMR in order to reliably
	 * resume if we're using pseudo-NMIs.
	 */
	arm_cpuidle_save_irq_context(&context);

	if (__cpu_suspend_enter(&state)) {
	if (__cpu_suspend_enter(&state)) {
		/* Call the suspend finisher */
		/* Call the suspend finisher */
		ret = fn(arg);
		ret = fn(arg);
@@ -126,6 +134,8 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
		RCU_NONIDLE(__cpu_suspend_exit());
		RCU_NONIDLE(__cpu_suspend_exit());
	}
	}


	arm_cpuidle_restore_irq_context(&context);

	unpause_graph_tracing();
	unpause_graph_tracing();


	/*
	/*
+7 −2
Original line number Original line Diff line number Diff line
@@ -335,10 +335,15 @@ int psci_cpu_suspend_enter(u32 state)
{
{
	int ret;
	int ret;


	if (!psci_power_state_loses_context(state))
	if (!psci_power_state_loses_context(state)) {
		struct arm_cpuidle_irq_context context;

		arm_cpuidle_save_irq_context(&context);
		ret = psci_ops.cpu_suspend(state, 0);
		ret = psci_ops.cpu_suspend(state, 0);
	else
		arm_cpuidle_restore_irq_context(&context);
	} else {
		ret = cpu_suspend(state, psci_suspend_finisher);
		ret = cpu_suspend(state, psci_suspend_finisher);
	}


	return ret;
	return ret;
}
}