diff mbox series

[v3,3/3] lib: sbi: Add SSE support for PMU events

Message ID 20240321155720.1966860-4-cleger@rivosinc.com
State Accepted
Headers show
Series Add support for Supervisor Software Events extension | expand

Commit Message

Clément Léger March 21, 2024, 3:57 p.m. UTC
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>
---
 include/sbi/sbi_pmu.h |  3 +++
 lib/sbi/sbi_pmu.c     | 51 +++++++++++++++++++++++++++++++++++++++++++
 lib/sbi/sbi_trap.c    |  6 +++++
 3 files changed, 60 insertions(+)

Comments

Samuel Holland April 5, 2024, 4:52 p.m. UTC | #1
Hi Clément,

On 2024-03-21 10:57 AM, Clément Léger wrote:
> 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>
> ---
>  include/sbi/sbi_pmu.h |  3 +++
>  lib/sbi/sbi_pmu.c     | 51 +++++++++++++++++++++++++++++++++++++++++++
>  lib/sbi/sbi_trap.c    |  6 +++++
>  3 files changed, 60 insertions(+)
> 
> diff --git a/include/sbi/sbi_pmu.h b/include/sbi/sbi_pmu.h
> index 7d32a4d..8b1e08c 100644
> --- a/include/sbi/sbi_pmu.h
> +++ b/include/sbi/sbi_pmu.h
> @@ -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
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 62a6465..b9e8454 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -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);

This needs to use sbi_pmu_irq_bit(), along with the all of the other new
references to 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;

nit: false instead of 0 for a Boolean variable.

>  }
>  
>  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));
> diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c
> index 624b45e..b4f3a17 100644
> --- a/lib/sbi/sbi_trap.c
> +++ b/lib/sbi/sbi_trap.c
> @@ -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;

This also needs to use sbi_pmu_irq_bit(). If you don't want to support
non-Sscofpmf implementations, then attempting to enable the SSE event should
fail for those implementations.

Regards,
Samuel

>  	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)
diff mbox series

Patch

diff --git a/include/sbi/sbi_pmu.h b/include/sbi/sbi_pmu.h
index 7d32a4d..8b1e08c 100644
--- a/include/sbi/sbi_pmu.h
+++ b/include/sbi/sbi_pmu.h
@@ -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
diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 62a6465..b9e8454 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -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));
diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c
index 624b45e..b4f3a17 100644
--- a/lib/sbi/sbi_trap.c
+++ b/lib/sbi/sbi_trap.c
@@ -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)