.. | .. |
---|
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 | |
---|