Loading arch/arm64/boot/dts/qcom/kona.dtsi +3 −3 Original line number Diff line number Diff line Loading @@ -955,9 +955,9 @@ BW_OPP_ENTRY( 2736, 4); /* 10437 MB/s */ }; llcc_pmu: llcc-pmu@9090000 { compatible = "qcom,qcom-llcc-pmu"; reg = <0x09090000 0x300>; llcc_pmu: llcc-pmu@9095000 { compatible = "qcom,llcc-pmu-ver2"; reg = <0x09095000 0x300>; reg-names = "lagg-base"; }; Loading drivers/perf/qcom_llcc_pmu.c +151 −19 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ */ #include <linux/of.h> #include <linux/of_device.h> #include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/io.h> Loading @@ -14,11 +15,17 @@ #include <linux/spinlock.h> #include <linux/ktime.h> enum llcc_pmu_version { LLCC_PMU_VER1, LLCC_PMU_VER2, }; struct llcc_pmu { struct pmu pmu; struct hlist_node node; void __iomem *lagg_base; struct perf_event event; enum llcc_pmu_version ver; }; #define MON_CFG(m) ((m)->lagg_base + 0x200) Loading @@ -26,9 +33,10 @@ struct llcc_pmu { #define to_llcc_pmu(ptr) (container_of(ptr, struct llcc_pmu, pmu)) #define LLCC_RD_EV 0x1000 #define ENABLE 0x01 #define ENABLE 0x1 #define CLEAR 0x10 #define DISABLE 0x00 #define CLEAR_POS 16 #define DISABLE 0x0 #define SCALING_FACTOR 0x3 #define NUM_COUNTERS NR_CPUS #define VALUE_MASK 0xFFFFFF Loading @@ -38,6 +46,91 @@ static unsigned int users; static raw_spinlock_t counter_lock; static raw_spinlock_t users_lock; static ktime_t last_read; static DEFINE_PER_CPU(unsigned int, users_alive); static void mon_disable(struct llcc_pmu *llccpmu, int cpu) { u32 reg; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return; } switch (llccpmu->ver) { case LLCC_PMU_VER1: writel_relaxed(DISABLE, MON_CFG(llccpmu)); break; case LLCC_PMU_VER2: reg = readl_relaxed(MON_CFG(llccpmu)); reg &= (DISABLE << cpu); writel_relaxed(reg, MON_CFG(llccpmu)); break; } } static void mon_clear(struct llcc_pmu *llccpmu, int cpu) { int clear_bit = CLEAR_POS + cpu; u32 reg; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return; } switch (llccpmu->ver) { case LLCC_PMU_VER1: writel_relaxed(CLEAR, MON_CFG(llccpmu)); break; case LLCC_PMU_VER2: reg = readl_relaxed(MON_CFG(llccpmu)); reg |= (ENABLE << clear_bit); writel_relaxed(reg, MON_CFG(llccpmu)); break; } } static void mon_enable(struct llcc_pmu *llccpmu, int cpu) { u32 reg; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return; } switch (llccpmu->ver) { case LLCC_PMU_VER1: writel_relaxed(ENABLE, MON_CFG(llccpmu)); break; case LLCC_PMU_VER2: reg = readl_relaxed(MON_CFG(llccpmu)); reg |= (ENABLE << cpu); writel_relaxed(reg, MON_CFG(llccpmu)); break; } } static unsigned long read_cnt(struct llcc_pmu *llccpmu, int cpu) { unsigned long value; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return -EINVAL; } switch (llccpmu->ver) { case LLCC_PMU_VER1: value = readl_relaxed(MON_CNT(llccpmu, cpu)); break; case LLCC_PMU_VER2: value = readl_relaxed(MON_CNT(llccpmu, cpu)); break; } return value; } static int qcom_llcc_event_init(struct perf_event *event) { Loading @@ -59,17 +152,26 @@ static void qcom_llcc_event_read(struct perf_event *event) ktime_t cur; raw_spin_lock_irqsave(&counter_lock, irq_flags); if (llccpmu->ver == LLCC_PMU_VER1) { cur = ktime_get(); if (ktime_ms_delta(cur, last_read) > 1) { writel_relaxed(DISABLE, MON_CFG(llccpmu)); mon_disable(llccpmu, cpu); for (i = 0; i < NUM_COUNTERS; i++) { raw = readl_relaxed(MON_CNT(llccpmu, i)); raw = read_cnt(llccpmu, i); raw &= VALUE_MASK; llcc_stats[i] += (u64) raw << SCALING_FACTOR; } last_read = cur; writel_relaxed(CLEAR, MON_CFG(llccpmu)); writel_relaxed(ENABLE, MON_CFG(llccpmu)); mon_clear(llccpmu, cpu); mon_enable(llccpmu, cpu); } } else { mon_disable(llccpmu, cpu); raw = read_cnt(llccpmu, cpu); raw &= VALUE_MASK; llcc_stats[cpu] += (u64) raw << SCALING_FACTOR; mon_clear(llccpmu, cpu); mon_enable(llccpmu, cpu); } if (!(event->hw.state & PERF_HES_STOPPED)) Loading @@ -93,11 +195,22 @@ static void qcom_llcc_event_stop(struct perf_event *event, int flags) static int qcom_llcc_event_add(struct perf_event *event, int flags) { struct llcc_pmu *llccpmu = to_llcc_pmu(event->pmu); unsigned int cpu_users; raw_spin_lock(&users_lock); if (llccpmu->ver == LLCC_PMU_VER1) { if (!users) writel_relaxed(ENABLE, MON_CFG(llccpmu)); mon_enable(llccpmu, event->cpu); users++; } else { cpu_users = per_cpu(users_alive, event->cpu); if (!cpu_users) mon_enable(llccpmu, event->cpu); cpu_users++; per_cpu(users_alive, event->cpu) = cpu_users; } raw_spin_unlock(&users_lock); event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; Loading @@ -111,11 +224,22 @@ static int qcom_llcc_event_add(struct perf_event *event, int flags) static void qcom_llcc_event_del(struct perf_event *event, int flags) { struct llcc_pmu *llccpmu = to_llcc_pmu(event->pmu); unsigned int cpu_users; raw_spin_lock(&users_lock); if (llccpmu->ver == LLCC_PMU_VER1) { users--; if (!users) writel_relaxed(DISABLE, MON_CFG(llccpmu)); mon_disable(llccpmu, event->cpu); } else { cpu_users = per_cpu(users_alive, event->cpu); cpu_users--; if (!cpu_users) mon_disable(llccpmu, event->cpu); per_cpu(users_alive, event->cpu) = cpu_users; } raw_spin_unlock(&users_lock); } Loading @@ -129,6 +253,13 @@ static int qcom_llcc_pmu_probe(struct platform_device *pdev) if (!llccpmu) return -ENOMEM; llccpmu->ver = (enum llcc_pmu_version) of_device_get_match_data(&pdev->dev); if (!llccpmu->ver) { pr_err("Unknown device type!\n"); return -ENODEV; } llccpmu->pmu = (struct pmu) { .task_ctx_nr = perf_invalid_context, Loading Loading @@ -163,7 +294,8 @@ static int qcom_llcc_pmu_probe(struct platform_device *pdev) } static const struct of_device_id qcom_llcc_pmu_match_table[] = { { .compatible = "qcom,qcom-llcc-pmu" }, { .compatible = "qcom,llcc-pmu-ver1", .data = (void *) LLCC_PMU_VER1 }, { .compatible = "qcom,llcc-pmu-ver2", .data = (void *) LLCC_PMU_VER2 }, {} }; Loading Loading
arch/arm64/boot/dts/qcom/kona.dtsi +3 −3 Original line number Diff line number Diff line Loading @@ -955,9 +955,9 @@ BW_OPP_ENTRY( 2736, 4); /* 10437 MB/s */ }; llcc_pmu: llcc-pmu@9090000 { compatible = "qcom,qcom-llcc-pmu"; reg = <0x09090000 0x300>; llcc_pmu: llcc-pmu@9095000 { compatible = "qcom,llcc-pmu-ver2"; reg = <0x09095000 0x300>; reg-names = "lagg-base"; }; Loading
drivers/perf/qcom_llcc_pmu.c +151 −19 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ */ #include <linux/of.h> #include <linux/of_device.h> #include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/io.h> Loading @@ -14,11 +15,17 @@ #include <linux/spinlock.h> #include <linux/ktime.h> enum llcc_pmu_version { LLCC_PMU_VER1, LLCC_PMU_VER2, }; struct llcc_pmu { struct pmu pmu; struct hlist_node node; void __iomem *lagg_base; struct perf_event event; enum llcc_pmu_version ver; }; #define MON_CFG(m) ((m)->lagg_base + 0x200) Loading @@ -26,9 +33,10 @@ struct llcc_pmu { #define to_llcc_pmu(ptr) (container_of(ptr, struct llcc_pmu, pmu)) #define LLCC_RD_EV 0x1000 #define ENABLE 0x01 #define ENABLE 0x1 #define CLEAR 0x10 #define DISABLE 0x00 #define CLEAR_POS 16 #define DISABLE 0x0 #define SCALING_FACTOR 0x3 #define NUM_COUNTERS NR_CPUS #define VALUE_MASK 0xFFFFFF Loading @@ -38,6 +46,91 @@ static unsigned int users; static raw_spinlock_t counter_lock; static raw_spinlock_t users_lock; static ktime_t last_read; static DEFINE_PER_CPU(unsigned int, users_alive); static void mon_disable(struct llcc_pmu *llccpmu, int cpu) { u32 reg; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return; } switch (llccpmu->ver) { case LLCC_PMU_VER1: writel_relaxed(DISABLE, MON_CFG(llccpmu)); break; case LLCC_PMU_VER2: reg = readl_relaxed(MON_CFG(llccpmu)); reg &= (DISABLE << cpu); writel_relaxed(reg, MON_CFG(llccpmu)); break; } } static void mon_clear(struct llcc_pmu *llccpmu, int cpu) { int clear_bit = CLEAR_POS + cpu; u32 reg; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return; } switch (llccpmu->ver) { case LLCC_PMU_VER1: writel_relaxed(CLEAR, MON_CFG(llccpmu)); break; case LLCC_PMU_VER2: reg = readl_relaxed(MON_CFG(llccpmu)); reg |= (ENABLE << clear_bit); writel_relaxed(reg, MON_CFG(llccpmu)); break; } } static void mon_enable(struct llcc_pmu *llccpmu, int cpu) { u32 reg; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return; } switch (llccpmu->ver) { case LLCC_PMU_VER1: writel_relaxed(ENABLE, MON_CFG(llccpmu)); break; case LLCC_PMU_VER2: reg = readl_relaxed(MON_CFG(llccpmu)); reg |= (ENABLE << cpu); writel_relaxed(reg, MON_CFG(llccpmu)); break; } } static unsigned long read_cnt(struct llcc_pmu *llccpmu, int cpu) { unsigned long value; if (!llccpmu->ver) { pr_err("LLCCPMU version not correct\n"); return -EINVAL; } switch (llccpmu->ver) { case LLCC_PMU_VER1: value = readl_relaxed(MON_CNT(llccpmu, cpu)); break; case LLCC_PMU_VER2: value = readl_relaxed(MON_CNT(llccpmu, cpu)); break; } return value; } static int qcom_llcc_event_init(struct perf_event *event) { Loading @@ -59,17 +152,26 @@ static void qcom_llcc_event_read(struct perf_event *event) ktime_t cur; raw_spin_lock_irqsave(&counter_lock, irq_flags); if (llccpmu->ver == LLCC_PMU_VER1) { cur = ktime_get(); if (ktime_ms_delta(cur, last_read) > 1) { writel_relaxed(DISABLE, MON_CFG(llccpmu)); mon_disable(llccpmu, cpu); for (i = 0; i < NUM_COUNTERS; i++) { raw = readl_relaxed(MON_CNT(llccpmu, i)); raw = read_cnt(llccpmu, i); raw &= VALUE_MASK; llcc_stats[i] += (u64) raw << SCALING_FACTOR; } last_read = cur; writel_relaxed(CLEAR, MON_CFG(llccpmu)); writel_relaxed(ENABLE, MON_CFG(llccpmu)); mon_clear(llccpmu, cpu); mon_enable(llccpmu, cpu); } } else { mon_disable(llccpmu, cpu); raw = read_cnt(llccpmu, cpu); raw &= VALUE_MASK; llcc_stats[cpu] += (u64) raw << SCALING_FACTOR; mon_clear(llccpmu, cpu); mon_enable(llccpmu, cpu); } if (!(event->hw.state & PERF_HES_STOPPED)) Loading @@ -93,11 +195,22 @@ static void qcom_llcc_event_stop(struct perf_event *event, int flags) static int qcom_llcc_event_add(struct perf_event *event, int flags) { struct llcc_pmu *llccpmu = to_llcc_pmu(event->pmu); unsigned int cpu_users; raw_spin_lock(&users_lock); if (llccpmu->ver == LLCC_PMU_VER1) { if (!users) writel_relaxed(ENABLE, MON_CFG(llccpmu)); mon_enable(llccpmu, event->cpu); users++; } else { cpu_users = per_cpu(users_alive, event->cpu); if (!cpu_users) mon_enable(llccpmu, event->cpu); cpu_users++; per_cpu(users_alive, event->cpu) = cpu_users; } raw_spin_unlock(&users_lock); event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; Loading @@ -111,11 +224,22 @@ static int qcom_llcc_event_add(struct perf_event *event, int flags) static void qcom_llcc_event_del(struct perf_event *event, int flags) { struct llcc_pmu *llccpmu = to_llcc_pmu(event->pmu); unsigned int cpu_users; raw_spin_lock(&users_lock); if (llccpmu->ver == LLCC_PMU_VER1) { users--; if (!users) writel_relaxed(DISABLE, MON_CFG(llccpmu)); mon_disable(llccpmu, event->cpu); } else { cpu_users = per_cpu(users_alive, event->cpu); cpu_users--; if (!cpu_users) mon_disable(llccpmu, event->cpu); per_cpu(users_alive, event->cpu) = cpu_users; } raw_spin_unlock(&users_lock); } Loading @@ -129,6 +253,13 @@ static int qcom_llcc_pmu_probe(struct platform_device *pdev) if (!llccpmu) return -ENOMEM; llccpmu->ver = (enum llcc_pmu_version) of_device_get_match_data(&pdev->dev); if (!llccpmu->ver) { pr_err("Unknown device type!\n"); return -ENODEV; } llccpmu->pmu = (struct pmu) { .task_ctx_nr = perf_invalid_context, Loading Loading @@ -163,7 +294,8 @@ static int qcom_llcc_pmu_probe(struct platform_device *pdev) } static const struct of_device_id qcom_llcc_pmu_match_table[] = { { .compatible = "qcom,qcom-llcc-pmu" }, { .compatible = "qcom,llcc-pmu-ver1", .data = (void *) LLCC_PMU_VER1 }, { .compatible = "qcom,llcc-pmu-ver2", .data = (void *) LLCC_PMU_VER2 }, {} }; Loading