lib: sbi: Add SSE support for PMU events
authorClément Léger <cleger@rivosinc.com>
Thu, 21 Mar 2024 15:57:18 +0000 (16:57 +0100)
committerAnup Patel <anup@brainfault.org>
Fri, 5 Apr 2024 11:39:39 +0000 (17:09 +0530)
Add SSE callbacks registration to PMU driver in order to disable
interrupt delegation for PMU interrupts. When interrupts are
undelegated send the PMU SSE event upon LCOFIP IRQ.

Signed-off-by: Clément Léger <cleger@rivosinc.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
include/sbi/sbi_pmu.h
lib/sbi/sbi_pmu.c
lib/sbi/sbi_trap.c

index 7d32a4da31d318ffee8f4cd99b8632216c8e0dbb..8b1e08c81f307b7ce01efe336b8e81d351a27e2a 100644 (file)
@@ -11,6 +11,7 @@
 #define __SBI_PMU_H__
 
 #include <sbi/sbi_types.h>
+#include <sbi/sbi_trap.h>
 
 struct sbi_scratch;
 
@@ -150,4 +151,6 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
 
 int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id);
 
+void sbi_pmu_ovf_irq();
+
 #endif
index 62a6465a1f1466aedf613adb3f0694723368996a..b9e845430fcb373dbb1ab8d0e9960954de07a985 100644 (file)
@@ -17,6 +17,7 @@
 #include <sbi/sbi_pmu.h>
 #include <sbi/sbi_scratch.h>
 #include <sbi/sbi_string.h>
+#include <sbi/sbi_sse.h>
 
 /** Information about hardware counters */
 struct sbi_pmu_hw_event {
@@ -62,6 +63,8 @@ struct sbi_pmu_hart_state {
        uint32_t active_events[SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX];
        /* Bitmap of firmware counters started */
        unsigned long fw_counters_started;
+       /* if true, SSE is enabled */
+       bool sse_enabled;
        /*
         * Counter values for SBI firmware events and event codes
         * for platform firmware events. Both are mutually exclusive
@@ -300,6 +303,16 @@ int sbi_pmu_add_raw_event_counter_map(uint64_t select, uint64_t select_mask, u32
                                    SBI_PMU_EVENT_RAW_IDX, cmap, select, select_mask);
 }
 
+void sbi_pmu_ovf_irq()
+{
+       /*
+        * We need to disable LCOFIP before returning to S-mode or we will loop
+        * on LCOFIP being triggered
+        */
+       csr_clear(CSR_MIE, MIP_LCOFIP);
+       sbi_sse_inject_event(SBI_SSE_EVENT_LOCAL_PMU);
+}
+
 static int pmu_ctr_enable_irq_hw(int ctr_idx)
 {
        unsigned long mhpmevent_csr;
@@ -575,6 +588,10 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
                }
        }
 
+       /* Clear MIP_LCOFIP to avoid spurious interrupts */
+       if (phs->sse_enabled)
+               csr_clear(CSR_MIP, MIP_LCOFIP);
+
        return ret;
 }
 
@@ -962,6 +979,7 @@ static void pmu_reset_event_map(struct sbi_pmu_hart_state *phs)
        for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
                phs->fw_counters_data[j] = 0;
        phs->fw_counters_started = 0;
+       phs->sse_enabled = 0;
 }
 
 const struct sbi_pmu_device *sbi_pmu_get_device(void)
@@ -993,6 +1011,37 @@ void sbi_pmu_exit(struct sbi_scratch *scratch)
        pmu_reset_event_map(phs);
 }
 
+static void pmu_sse_enable(uint32_t event_id)
+{
+       struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr();
+
+       phs->sse_enabled = true;
+       csr_clear(CSR_MIDELEG, sbi_pmu_irq_bit());
+       csr_clear(CSR_MIP, MIP_LCOFIP);
+       csr_set(CSR_MIE, MIP_LCOFIP);
+}
+
+static void pmu_sse_disable(uint32_t event_id)
+{
+       struct sbi_pmu_hart_state *phs = pmu_thishart_state_ptr();
+
+       csr_clear(CSR_MIE, MIP_LCOFIP);
+       csr_clear(CSR_MIP, MIP_LCOFIP);
+       csr_set(CSR_MIDELEG, sbi_pmu_irq_bit());
+       phs->sse_enabled = false;
+}
+
+static void pmu_sse_complete(uint32_t event_id)
+{
+       csr_set(CSR_MIE, MIP_LCOFIP);
+}
+
+static const struct sbi_sse_cb_ops pmu_sse_cb_ops = {
+       .enable_cb = pmu_sse_enable,
+       .disable_cb = pmu_sse_disable,
+       .complete_cb = pmu_sse_complete,
+};
+
 int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot)
 {
        int hpm_count = sbi_fls(sbi_hart_mhpm_mask(scratch));
@@ -1032,6 +1081,8 @@ int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot)
                total_ctrs = num_hw_ctrs + SBI_PMU_FW_CTR_MAX;
        }
 
+       sbi_sse_set_cb_ops(SBI_SSE_EVENT_LOCAL_PMU, &pmu_sse_cb_ops);
+
        phs = pmu_get_hart_state_ptr(scratch);
        if (!phs) {
                phs = sbi_zalloc(sizeof(*phs));
index 624b45e0856cdc3ee8dcabbe3cd2c2037cd6cf2c..b4f3a176d6b1669b2b3d8a4d6b021e334c755e31 100644 (file)
@@ -223,6 +223,9 @@ static int sbi_trap_nonaia_irq(unsigned long irq)
        case IRQ_M_SOFT:
                sbi_ipi_process();
                break;
+       case IRQ_PMU_OVF:
+               sbi_pmu_ovf_irq();
+               break;
        case IRQ_M_EXT:
                return sbi_irqchip_process();
        default:
@@ -246,6 +249,9 @@ static int sbi_trap_aia_irq(void)
                case IRQ_M_SOFT:
                        sbi_ipi_process();
                        break;
+               case IRQ_PMU_OVF:
+                       sbi_pmu_ovf_irq();
+                       break;
                case IRQ_M_EXT:
                        rc = sbi_irqchip_process();
                        if (rc)