return delta;
 }
 
-static void riscv_pmu_stop(struct perf_event *event, int flags)
+void riscv_pmu_stop(struct perf_event *event, int flags)
 {
        struct hw_perf_event *hwc = &event->hw;
        struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
        return overflow;
 }
 
-static void riscv_pmu_start(struct perf_event *event, int flags)
+void riscv_pmu_start(struct perf_event *event, int flags)
 {
        struct hw_perf_event *hwc = &event->hw;
        struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
 
 #include <linux/irqdomain.h>
 #include <linux/of_irq.h>
 #include <linux/of.h>
+#include <linux/cpu_pm.h>
 
 #include <asm/sbi.h>
 #include <asm/hwcap.h>
        return 0;
 }
 
+#ifdef CONFIG_CPU_PM
+static int riscv_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
+                               void *v)
+{
+       struct riscv_pmu *rvpmu = container_of(b, struct riscv_pmu, riscv_pm_nb);
+       struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
+       int enabled = bitmap_weight(cpuc->used_hw_ctrs, RISCV_MAX_COUNTERS);
+       struct perf_event *event;
+       int idx;
+
+       if (!enabled)
+               return NOTIFY_OK;
+
+       for (idx = 0; idx < RISCV_MAX_COUNTERS; idx++) {
+               event = cpuc->events[idx];
+               if (!event)
+                       continue;
+
+               switch (cmd) {
+               case CPU_PM_ENTER:
+                       /*
+                        * Stop and update the counter
+                        */
+                       riscv_pmu_stop(event, PERF_EF_UPDATE);
+                       break;
+               case CPU_PM_EXIT:
+               case CPU_PM_ENTER_FAILED:
+                       /*
+                        * Restore and enable the counter.
+                        *
+                        * Requires RCU read locking to be functional,
+                        * wrap the call within RCU_NONIDLE to make the
+                        * RCU subsystem aware this cpu is not idle from
+                        * an RCU perspective for the riscv_pmu_start() call
+                        * duration.
+                        */
+                       RCU_NONIDLE(riscv_pmu_start(event, PERF_EF_RELOAD));
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int riscv_pm_pmu_register(struct riscv_pmu *pmu)
+{
+       pmu->riscv_pm_nb.notifier_call = riscv_pm_pmu_notify;
+       return cpu_pm_register_notifier(&pmu->riscv_pm_nb);
+}
+
+static void riscv_pm_pmu_unregister(struct riscv_pmu *pmu)
+{
+       cpu_pm_unregister_notifier(&pmu->riscv_pm_nb);
+}
+#else
+static inline int riscv_pm_pmu_register(struct riscv_pmu *pmu) { return 0; }
+static inline void riscv_pm_pmu_unregister(struct riscv_pmu *pmu) { }
+#endif
+
+static void riscv_pmu_destroy(struct riscv_pmu *pmu)
+{
+       riscv_pm_pmu_unregister(pmu);
+       cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
+}
+
 static int pmu_sbi_device_probe(struct platform_device *pdev)
 {
        struct riscv_pmu *pmu = NULL;
        if (ret)
                return ret;
 
+       ret = riscv_pm_pmu_register(pmu);
+       if (ret)
+               goto out_unregister;
+
        ret = perf_pmu_register(&pmu->pmu, "cpu", PERF_TYPE_RAW);
-       if (ret) {
-               cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
-               return ret;
-       }
+       if (ret)
+               goto out_unregister;
 
        return 0;
 
+out_unregister:
+       riscv_pmu_destroy(pmu);
+
 out_free:
        kfree(pmu);
        return ret;
 
 
        struct cpu_hw_events    __percpu *hw_events;
        struct hlist_node       node;
+       struct notifier_block   riscv_pm_nb;
 };
 
 #define to_riscv_pmu(p) (container_of(p, struct riscv_pmu, pmu))
+
+void riscv_pmu_start(struct perf_event *event, int flags);
+void riscv_pmu_stop(struct perf_event *event, int flags);
 unsigned long riscv_pmu_ctr_read_csr(unsigned long csr);
 int riscv_pmu_event_set_period(struct perf_event *event);
 uint64_t riscv_pmu_ctr_get_width_mask(struct perf_event *event);