};
 
 /*
- * RISC-V doesn't have hetergenous harts yet. This need to be part of
+ * RISC-V doesn't have heterogeneous harts yet. This need to be part of
  * per_cpu in case of harts with different pmu counters
  */
 static union sbi_pmu_ctr_info *pmu_ctr_list;
 static unsigned int riscv_pmu_irq_num;
 static unsigned int riscv_pmu_irq;
 
+/* Cache the available counters in a bitmask */
+static unsigned long cmask;
+
 struct sbi_pmu_event_data {
        union {
                union {
        return (info->type == SBI_PMU_CTR_TYPE_FW) ? true : false;
 }
 
+/*
+ * Returns the counter width of a programmable counter and number of hardware
+ * counters. As we don't support heterogeneous CPUs yet, it is okay to just
+ * return the counter width of the first programmable counter.
+ */
+int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr)
+{
+       int i;
+       union sbi_pmu_ctr_info *info;
+       u32 hpm_width = 0, hpm_count = 0;
+
+       if (!cmask)
+               return -EINVAL;
+
+       for_each_set_bit(i, &cmask, RISCV_MAX_COUNTERS) {
+               info = &pmu_ctr_list[i];
+               if (!info)
+                       continue;
+               if (!hpm_width && info->csr != CSR_CYCLE && info->csr != CSR_INSTRET)
+                       hpm_width = info->width;
+               if (info->type == SBI_PMU_CTR_TYPE_HW)
+                       hpm_count++;
+       }
+
+       *hw_ctr_width = hpm_width;
+       *num_hw_ctr = hpm_count;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(riscv_pmu_get_hpm_info);
+
 static int pmu_sbi_ctr_get_idx(struct perf_event *event)
 {
        struct hw_perf_event *hwc = &event->hw;
 static int pmu_sbi_device_probe(struct platform_device *pdev)
 {
        struct riscv_pmu *pmu = NULL;
-       unsigned long cmask = 0;
        int ret = -ENODEV;
        int num_counters;