From: Junhao He hejunhao3@huawei.com
driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB7OM0 CVE: NA
----------------------------------------------------------------------
On HiSilicon HIP10C platform, the DDRC PMU interrupt registers offset is modified, causing the interrupt handler to fail to properly handle counter overflows.
This patch tries to fix this by correcting the HIP10C platform DDRC PMU interrupt registers offset (including mask/status/clear registers).
Signed-off-by: Junhao He hejunhao3@huawei.com Signed-off-by: Qizhi Zhang zhangqizhi3@h-partners.com Signed-off-by: Yushan Wang wangyushan12@huawei.com --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 70 +++++++++++++++++-- drivers/perf/hisilicon/hisi_uncore_pmu.h | 1 + 2 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ee020669d134..816770ace86f 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -43,6 +43,16 @@ #define DDRC_V2_EVENT_TYPE 0xe74 #define DDRC_V2_PERF_CTRL 0xeA0
+/* + * HIP10C platform v2 PMU DDRC interrupt registers definition + * On HIP10C platform, the DDRC PMU interrupt register offset is + * modified, causing the interrupt handler to fail to properly + * handle counter overflows. + */ +#define DDRC_V2_HIP10C_INT_MASK 0x534 +#define DDRC_V2_HIP10C_INT_STATUS 0x538 +#define DDRC_V2_HIP10C_INT_CLEAR 0x53C + /* DDRC has 8-counters */ #define DDRC_NR_COUNTERS 0x8 #define DDRC_V1_PERF_CTRL_EN 0x2 @@ -58,11 +68,39 @@ */ #define GET_DDRC_EVENTID(hwc) (hwc->config_base & 0x7)
+#define HISI_DDRC_PMU_NORMAL_PMU 0x0 /* HiSilicon normal PMU */ +#define HISI_DDRC_PMU_INCORRECT_INT_REGS BIT(0) /* HiSilicon HIP10C PMU */ + static const u32 ddrc_reg_off[] = { DDRC_FLUX_WR, DDRC_FLUX_RD, DDRC_FLUX_WCMD, DDRC_FLUX_RCMD, DDRC_PRE_CMD, DDRC_ACT_CMD, DDRC_RNK_CHG, DDRC_RW_CHG };
+static struct acpi_platform_list hisi_uncore_plat_info[] = { + /* HiSilicon Hip10C Platform */ + {"HISI ", "HIP10C ", 0, ACPI_SIG_DSDT, greater_than_or_equal, + "Erratum #162400501", HISI_DDRC_PMU_INCORRECT_INT_REGS}, + { } +}; + +static void hisi_ddrc_pmu_check_errata(struct platform_device *pdev, struct hisi_pmu *ddrc_pmu) +{ + int idx; + + idx = acpi_match_platform_list(hisi_uncore_plat_info); + if (idx >= 0) + ddrc_pmu->errata = hisi_uncore_plat_info[idx].data; + else + ddrc_pmu->errata = HISI_DDRC_PMU_NORMAL_PMU; + + dev_dbg(ddrc_pmu->dev, "errata mask %#x\n", ddrc_pmu->errata); +} + +static bool hisi_ddrc_pmu_has_erratum(struct hisi_pmu *ddrc_pmu, u32 erratum) +{ + return !!(ddrc_pmu->errata & erratum); +} + /* * Select the counter register offset using the counter index. * In PMU v1, there are no programmable counter, the count @@ -248,21 +286,29 @@ static void hisi_ddrc_pmu_v1_disable_counter_int(struct hisi_pmu *ddrc_pmu, static void hisi_ddrc_pmu_v2_enable_counter_int(struct hisi_pmu *ddrc_pmu, struct hw_perf_event *hwc) { + u32 int_mask = DDRC_V2_INT_MASK; u32 val;
- val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); + if (hisi_ddrc_pmu_has_erratum(ddrc_pmu, HISI_DDRC_PMU_INCORRECT_INT_REGS)) + int_mask = DDRC_V2_HIP10C_INT_MASK; + + val = readl(ddrc_pmu->base + int_mask); val &= ~(1 << hwc->idx); - writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); + writel(val, ddrc_pmu->base + int_mask); }
static void hisi_ddrc_pmu_v2_disable_counter_int(struct hisi_pmu *ddrc_pmu, struct hw_perf_event *hwc) { + u32 int_mask = DDRC_V2_INT_MASK; u32 val;
- val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); + if (hisi_ddrc_pmu_has_erratum(ddrc_pmu, HISI_DDRC_PMU_INCORRECT_INT_REGS)) + int_mask = DDRC_V2_HIP10C_INT_MASK; + + val = readl(ddrc_pmu->base + int_mask); val |= 1 << hwc->idx; - writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); + writel(val, ddrc_pmu->base + int_mask); }
static u32 hisi_ddrc_pmu_v1_get_int_status(struct hisi_pmu *ddrc_pmu) @@ -278,13 +324,23 @@ static void hisi_ddrc_pmu_v1_clear_int_status(struct hisi_pmu *ddrc_pmu,
static u32 hisi_ddrc_pmu_v2_get_int_status(struct hisi_pmu *ddrc_pmu) { - return readl(ddrc_pmu->base + DDRC_V2_INT_STATUS); + u32 int_status = DDRC_V2_INT_STATUS; + + if (hisi_ddrc_pmu_has_erratum(ddrc_pmu, HISI_DDRC_PMU_INCORRECT_INT_REGS)) + int_status = DDRC_V2_HIP10C_INT_STATUS; + + return readl(ddrc_pmu->base + int_status); }
static void hisi_ddrc_pmu_v2_clear_int_status(struct hisi_pmu *ddrc_pmu, int idx) { - writel(1 << idx, ddrc_pmu->base + DDRC_V2_INT_CLEAR); + u32 int_clear = DDRC_V2_INT_CLEAR; + + if (hisi_ddrc_pmu_has_erratum(ddrc_pmu, HISI_DDRC_PMU_INCORRECT_INT_REGS)) + int_clear = DDRC_V2_HIP10C_INT_CLEAR; + + writel(1 << idx, ddrc_pmu->base + int_clear); }
static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { @@ -464,6 +520,8 @@ static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret;
+ hisi_ddrc_pmu_check_errata(pdev, ddrc_pmu); + if (ddrc_pmu->identifier >= HISI_PMU_V2) { ddrc_pmu->counter_bits = 48; ddrc_pmu->check_event = DDRC_V2_NR_EVENTS; diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 9cf06c80acf4..eeacf2e89568 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -107,6 +107,7 @@ struct hisi_pmu { /* check event code range */ int check_event; u32 identifier; + u32 errata; };
int hisi_uncore_pmu_get_event_idx(struct perf_event *event);
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/14039 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/N...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/14039 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/N...