#endif
 #include <asm/accounting.h>
 #include <asm/hmi.h>
+#include <asm/cpuidle.h>
 
 register struct paca_struct *local_paca asm("r13");
 
        struct paca_struct **thread_sibling_pacas;
        /* The PSSCR value that the kernel requested before going to stop */
        u64 requested_psscr;
+
+       /*
+        * Save area for additional SPRs that need to be
+        * saved/restored during cpuidle stop.
+        */
+       struct stop_sprs stop_sprs;
 #endif
 
 #ifdef CONFIG_PPC_STD_MMU_64
 
        OFFSET(PACA_SUBCORE_SIBLING_MASK, paca_struct, subcore_sibling_mask);
        OFFSET(PACA_SIBLING_PACA_PTRS, paca_struct, thread_sibling_pacas);
        OFFSET(PACA_REQ_PSSCR, paca_struct, requested_psscr);
+#define STOP_SPR(x, f) OFFSET(x, paca_struct, stop_sprs.f)
+       STOP_SPR(STOP_PID, pid);
+       STOP_SPR(STOP_LDBAR, ldbar);
+       STOP_SPR(STOP_FSCR, fscr);
+       STOP_SPR(STOP_HFSCR, hfscr);
+       STOP_SPR(STOP_MMCR1, mmcr1);
+       STOP_SPR(STOP_MMCR2, mmcr2);
+       STOP_SPR(STOP_MMCRA, mmcra);
 #endif
 
        DEFINE(PPC_DBELL_SERVER, PPC_DBELL_SERVER);
 
        std     r3,_WORT(r1)
        mfspr   r3,SPRN_WORC
        std     r3,_WORC(r1)
+/*
+ * On POWER9, there are idle states such as stop4, invoked via cpuidle,
+ * that lose hypervisor resources. In such cases, we need to save
+ * additional SPRs before entering those idle states so that they can
+ * be restored to their older values on wakeup from the idle state.
+ *
+ * On POWER8, the only such deep idle state is winkle which is used
+ * only in the context of CPU-Hotplug, where these additional SPRs are
+ * reinitiazed to a sane value. Hence there is no need to save/restore
+ * these SPRs.
+ */
+BEGIN_FTR_SECTION
+       blr
+END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300)
+
+power9_save_additional_sprs:
+       mfspr   r3, SPRN_PID
+       mfspr   r4, SPRN_LDBAR
+       std     r3, STOP_PID(r13)
+       std     r4, STOP_LDBAR(r13)
 
+       mfspr   r3, SPRN_FSCR
+       mfspr   r4, SPRN_HFSCR
+       std     r3, STOP_FSCR(r13)
+       std     r4, STOP_HFSCR(r13)
+
+       mfspr   r3, SPRN_MMCRA
+       mfspr   r4, SPRN_MMCR1
+       std     r3, STOP_MMCRA(r13)
+       std     r4, STOP_MMCR1(r13)
+
+       mfspr   r3, SPRN_MMCR2
+       std     r3, STOP_MMCR2(r13)
+       blr
+
+power9_restore_additional_sprs:
+       ld      r3,_LPCR(r1)
+       ld      r4, STOP_PID(r13)
+       mtspr   SPRN_LPCR,r3
+       mtspr   SPRN_PID, r4
+
+       ld      r3, STOP_LDBAR(r13)
+       ld      r4, STOP_FSCR(r13)
+       mtspr   SPRN_LDBAR, r3
+       mtspr   SPRN_FSCR, r4
+
+       ld      r3, STOP_HFSCR(r13)
+       ld      r4, STOP_MMCRA(r13)
+       mtspr   SPRN_HFSCR, r3
+       mtspr   SPRN_MMCRA, r4
+       /* We have already restored PACA_MMCR0 */
+       ld      r3, STOP_MMCR1(r13)
+       ld      r4, STOP_MMCR2(r13)
+       mtspr   SPRN_MMCR1, r3
+       mtspr   SPRN_MMCR2, r4
        blr
 
 /*
        mtctr   r12
        bctrl
 
+/*
+ * On POWER9, we can come here on wakeup from a cpuidle stop state.
+ * Hence restore the additional SPRs to the saved value.
+ *
+ * On POWER8, we come here only on winkle. Since winkle is used
+ * only in the case of CPU-Hotplug, we don't need to restore
+ * the additional SPRs.
+ */
 BEGIN_FTR_SECTION
-       ld      r4,_LPCR(r1)
-       mtspr   SPRN_LPCR,r4
+       bl      power9_restore_additional_sprs
 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
 hypervisor_state_restored: