| .. | .. |
|---|
| 95 | 95 | #define SMMU_PMCG_PA_SHIFT 12 |
|---|
| 96 | 96 | |
|---|
| 97 | 97 | #define SMMU_PMCG_EVCNTR_RDONLY BIT(0) |
|---|
| 98 | +#define SMMU_PMCG_HARDEN_DISABLE BIT(1) |
|---|
| 98 | 99 | |
|---|
| 99 | 100 | static int cpuhp_state_num; |
|---|
| 100 | 101 | |
|---|
| .. | .. |
|---|
| 138 | 139 | writel(SMMU_PMCG_CR_ENABLE, smmu_pmu->reg_base + SMMU_PMCG_CR); |
|---|
| 139 | 140 | } |
|---|
| 140 | 141 | |
|---|
| 142 | +static int smmu_pmu_apply_event_filter(struct smmu_pmu *smmu_pmu, |
|---|
| 143 | + struct perf_event *event, int idx); |
|---|
| 144 | + |
|---|
| 145 | +static inline void smmu_pmu_enable_quirk_hip08_09(struct pmu *pmu) |
|---|
| 146 | +{ |
|---|
| 147 | + struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu); |
|---|
| 148 | + unsigned int idx; |
|---|
| 149 | + |
|---|
| 150 | + for_each_set_bit(idx, smmu_pmu->used_counters, smmu_pmu->num_counters) |
|---|
| 151 | + smmu_pmu_apply_event_filter(smmu_pmu, smmu_pmu->events[idx], idx); |
|---|
| 152 | + |
|---|
| 153 | + smmu_pmu_enable(pmu); |
|---|
| 154 | +} |
|---|
| 155 | + |
|---|
| 141 | 156 | static inline void smmu_pmu_disable(struct pmu *pmu) |
|---|
| 142 | 157 | { |
|---|
| 143 | 158 | struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu); |
|---|
| 144 | 159 | |
|---|
| 145 | 160 | writel(0, smmu_pmu->reg_base + SMMU_PMCG_CR); |
|---|
| 146 | 161 | writel(0, smmu_pmu->reg_base + SMMU_PMCG_IRQ_CTRL); |
|---|
| 162 | +} |
|---|
| 163 | + |
|---|
| 164 | +static inline void smmu_pmu_disable_quirk_hip08_09(struct pmu *pmu) |
|---|
| 165 | +{ |
|---|
| 166 | + struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu); |
|---|
| 167 | + unsigned int idx; |
|---|
| 168 | + |
|---|
| 169 | + /* |
|---|
| 170 | + * The global disable of PMU sometimes fail to stop the counting. |
|---|
| 171 | + * Harden this by writing an invalid event type to each used counter |
|---|
| 172 | + * to forcibly stop counting. |
|---|
| 173 | + */ |
|---|
| 174 | + for_each_set_bit(idx, smmu_pmu->used_counters, smmu_pmu->num_counters) |
|---|
| 175 | + writel(0xffff, smmu_pmu->reg_base + SMMU_PMCG_EVTYPER(idx)); |
|---|
| 176 | + |
|---|
| 177 | + smmu_pmu_disable(pmu); |
|---|
| 147 | 178 | } |
|---|
| 148 | 179 | |
|---|
| 149 | 180 | static inline void smmu_pmu_counter_set_value(struct smmu_pmu *smmu_pmu, |
|---|
| .. | .. |
|---|
| 719 | 750 | switch (model) { |
|---|
| 720 | 751 | case IORT_SMMU_V3_PMCG_HISI_HIP08: |
|---|
| 721 | 752 | /* HiSilicon Erratum 162001800 */ |
|---|
| 722 | | - smmu_pmu->options |= SMMU_PMCG_EVCNTR_RDONLY; |
|---|
| 753 | + smmu_pmu->options |= SMMU_PMCG_EVCNTR_RDONLY | SMMU_PMCG_HARDEN_DISABLE; |
|---|
| 754 | + break; |
|---|
| 755 | + case IORT_SMMU_V3_PMCG_HISI_HIP09: |
|---|
| 756 | + smmu_pmu->options |= SMMU_PMCG_HARDEN_DISABLE; |
|---|
| 723 | 757 | break; |
|---|
| 724 | 758 | } |
|---|
| 725 | 759 | |
|---|
| .. | .. |
|---|
| 806 | 840 | |
|---|
| 807 | 841 | smmu_pmu_get_acpi_options(smmu_pmu); |
|---|
| 808 | 842 | |
|---|
| 843 | + /* |
|---|
| 844 | + * For platforms suffer this quirk, the PMU disable sometimes fails to |
|---|
| 845 | + * stop the counters. This will leads to inaccurate or error counting. |
|---|
| 846 | + * Forcibly disable the counters with these quirk handler. |
|---|
| 847 | + */ |
|---|
| 848 | + if (smmu_pmu->options & SMMU_PMCG_HARDEN_DISABLE) { |
|---|
| 849 | + smmu_pmu->pmu.pmu_enable = smmu_pmu_enable_quirk_hip08_09; |
|---|
| 850 | + smmu_pmu->pmu.pmu_disable = smmu_pmu_disable_quirk_hip08_09; |
|---|
| 851 | + } |
|---|
| 852 | + |
|---|
| 809 | 853 | /* Pick one CPU to be the preferred one to use */ |
|---|
| 810 | 854 | smmu_pmu->on_cpu = raw_smp_processor_id(); |
|---|
| 811 | 855 | WARN_ON(irq_set_affinity_hint(smmu_pmu->irq, |
|---|
| .. | .. |
|---|
| 870 | 914 | |
|---|
| 871 | 915 | static int __init arm_smmu_pmu_init(void) |
|---|
| 872 | 916 | { |
|---|
| 917 | + int ret; |
|---|
| 918 | + |
|---|
| 873 | 919 | cpuhp_state_num = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, |
|---|
| 874 | 920 | "perf/arm/pmcg:online", |
|---|
| 875 | 921 | NULL, |
|---|
| .. | .. |
|---|
| 877 | 923 | if (cpuhp_state_num < 0) |
|---|
| 878 | 924 | return cpuhp_state_num; |
|---|
| 879 | 925 | |
|---|
| 880 | | - return platform_driver_register(&smmu_pmu_driver); |
|---|
| 926 | + ret = platform_driver_register(&smmu_pmu_driver); |
|---|
| 927 | + if (ret) |
|---|
| 928 | + cpuhp_remove_multi_state(cpuhp_state_num); |
|---|
| 929 | + |
|---|
| 930 | + return ret; |
|---|
| 881 | 931 | } |
|---|
| 882 | 932 | module_init(arm_smmu_pmu_init); |
|---|
| 883 | 933 | |
|---|