diff options
author | Suzuki K. Poulose <suzuki.poulose@arm.com> | 2015-03-18 08:24:39 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2015-03-27 09:44:24 -0400 |
commit | fc17c839448e498393009e06ca30a204eefaccee (patch) | |
tree | 9e83c56f317e2ad5bfde9341c3711df57665f95b /drivers/bus | |
parent | f6b9e83ce05e362f4190cf2b4281d85cd094e541 (diff) |
arm-cci: Abstract the CCI400 PMU specific definitions
CCI400 has different event specifications for PMU, for revsion
0 and revision 1. As of now, we check the revision every single
time before using the parameters for the PMU. This patch abstracts
the details of the pmu models in a struct (cci_pmu_model) and
stores the information in cci_pmu at initialisation time, avoiding
multiple probe operations.
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Acked-by: Punit Agrawal <punit.agrawal@arm.com>
Reviewed-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K. Poulose <suzuki.poulose@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/arm-cci.c | 141 |
1 files changed, 81 insertions, 60 deletions
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index 5d29ec34078e..ae3864d95e6c 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c | |||
@@ -79,19 +79,38 @@ static const struct of_device_id arm_cci_matches[] = { | |||
79 | 79 | ||
80 | #define CCI_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */ | 80 | #define CCI_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */ |
81 | 81 | ||
82 | /* Types of interfaces that can generate events */ | ||
83 | enum { | ||
84 | CCI_IF_SLAVE, | ||
85 | CCI_IF_MASTER, | ||
86 | CCI_IF_MAX, | ||
87 | }; | ||
88 | |||
89 | struct event_range { | ||
90 | u32 min; | ||
91 | u32 max; | ||
92 | }; | ||
93 | |||
82 | struct cci_pmu_hw_events { | 94 | struct cci_pmu_hw_events { |
83 | struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; | 95 | struct perf_event *events[CCI_PMU_MAX_HW_EVENTS]; |
84 | unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; | 96 | unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)]; |
85 | raw_spinlock_t pmu_lock; | 97 | raw_spinlock_t pmu_lock; |
86 | }; | 98 | }; |
87 | 99 | ||
100 | struct cci_pmu_model { | ||
101 | char *name; | ||
102 | struct event_range event_ranges[CCI_IF_MAX]; | ||
103 | }; | ||
104 | |||
105 | static struct cci_pmu_model cci_pmu_models[]; | ||
106 | |||
88 | struct cci_pmu { | 107 | struct cci_pmu { |
89 | void __iomem *base; | 108 | void __iomem *base; |
90 | struct pmu pmu; | 109 | struct pmu pmu; |
91 | int nr_irqs; | 110 | int nr_irqs; |
92 | int irqs[CCI_PMU_MAX_HW_EVENTS]; | 111 | int irqs[CCI_PMU_MAX_HW_EVENTS]; |
93 | unsigned long active_irqs; | 112 | unsigned long active_irqs; |
94 | struct pmu_port_event_ranges *port_ranges; | 113 | const struct cci_pmu_model *model; |
95 | struct cci_pmu_hw_events hw_events; | 114 | struct cci_pmu_hw_events hw_events; |
96 | struct platform_device *plat_device; | 115 | struct platform_device *plat_device; |
97 | int num_events; | 116 | int num_events; |
@@ -152,53 +171,11 @@ enum cci400_perf_events { | |||
152 | #define CCI_REV_R1_MASTER_PORT_MIN_EV 0x00 | 171 | #define CCI_REV_R1_MASTER_PORT_MIN_EV 0x00 |
153 | #define CCI_REV_R1_MASTER_PORT_MAX_EV 0x11 | 172 | #define CCI_REV_R1_MASTER_PORT_MAX_EV 0x11 |
154 | 173 | ||
155 | struct pmu_port_event_ranges { | ||
156 | u8 slave_min; | ||
157 | u8 slave_max; | ||
158 | u8 master_min; | ||
159 | u8 master_max; | ||
160 | }; | ||
161 | |||
162 | static struct pmu_port_event_ranges port_event_range[] = { | ||
163 | [CCI_REV_R0] = { | ||
164 | .slave_min = CCI_REV_R0_SLAVE_PORT_MIN_EV, | ||
165 | .slave_max = CCI_REV_R0_SLAVE_PORT_MAX_EV, | ||
166 | .master_min = CCI_REV_R0_MASTER_PORT_MIN_EV, | ||
167 | .master_max = CCI_REV_R0_MASTER_PORT_MAX_EV, | ||
168 | }, | ||
169 | [CCI_REV_R1] = { | ||
170 | .slave_min = CCI_REV_R1_SLAVE_PORT_MIN_EV, | ||
171 | .slave_max = CCI_REV_R1_SLAVE_PORT_MAX_EV, | ||
172 | .master_min = CCI_REV_R1_MASTER_PORT_MIN_EV, | ||
173 | .master_max = CCI_REV_R1_MASTER_PORT_MAX_EV, | ||
174 | }, | ||
175 | }; | ||
176 | |||
177 | /* | ||
178 | * Export different PMU names for the different revisions so userspace knows | ||
179 | * because the event ids are different | ||
180 | */ | ||
181 | static char *const pmu_names[] = { | ||
182 | [CCI_REV_R0] = "CCI_400", | ||
183 | [CCI_REV_R1] = "CCI_400_r1", | ||
184 | }; | ||
185 | |||
186 | static int pmu_is_valid_slave_event(u8 ev_code) | ||
187 | { | ||
188 | return pmu->port_ranges->slave_min <= ev_code && | ||
189 | ev_code <= pmu->port_ranges->slave_max; | ||
190 | } | ||
191 | |||
192 | static int pmu_is_valid_master_event(u8 ev_code) | ||
193 | { | ||
194 | return pmu->port_ranges->master_min <= ev_code && | ||
195 | ev_code <= pmu->port_ranges->master_max; | ||
196 | } | ||
197 | |||
198 | static int pmu_validate_hw_event(u8 hw_event) | 174 | static int pmu_validate_hw_event(u8 hw_event) |
199 | { | 175 | { |
200 | u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event); | 176 | u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event); |
201 | u8 ev_code = CCI_PMU_EVENT_CODE(hw_event); | 177 | u8 ev_code = CCI_PMU_EVENT_CODE(hw_event); |
178 | int if_type; | ||
202 | 179 | ||
203 | switch (ev_source) { | 180 | switch (ev_source) { |
204 | case CCI_PORT_S0: | 181 | case CCI_PORT_S0: |
@@ -207,18 +184,22 @@ static int pmu_validate_hw_event(u8 hw_event) | |||
207 | case CCI_PORT_S3: | 184 | case CCI_PORT_S3: |
208 | case CCI_PORT_S4: | 185 | case CCI_PORT_S4: |
209 | /* Slave Interface */ | 186 | /* Slave Interface */ |
210 | if (pmu_is_valid_slave_event(ev_code)) | 187 | if_type = CCI_IF_SLAVE; |
211 | return hw_event; | ||
212 | break; | 188 | break; |
213 | case CCI_PORT_M0: | 189 | case CCI_PORT_M0: |
214 | case CCI_PORT_M1: | 190 | case CCI_PORT_M1: |
215 | case CCI_PORT_M2: | 191 | case CCI_PORT_M2: |
216 | /* Master Interface */ | 192 | /* Master Interface */ |
217 | if (pmu_is_valid_master_event(ev_code)) | 193 | if_type = CCI_IF_MASTER; |
218 | return hw_event; | ||
219 | break; | 194 | break; |
195 | default: | ||
196 | return -ENOENT; | ||
220 | } | 197 | } |
221 | 198 | ||
199 | if (ev_code >= pmu->model->event_ranges[if_type].min && | ||
200 | ev_code <= pmu->model->event_ranges[if_type].max) | ||
201 | return hw_event; | ||
202 | |||
222 | return -ENOENT; | 203 | return -ENOENT; |
223 | } | 204 | } |
224 | 205 | ||
@@ -234,11 +215,9 @@ static int probe_cci_revision(void) | |||
234 | return CCI_REV_R1; | 215 | return CCI_REV_R1; |
235 | } | 216 | } |
236 | 217 | ||
237 | static struct pmu_port_event_ranges *port_range_by_rev(void) | 218 | static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev) |
238 | { | 219 | { |
239 | int rev = probe_cci_revision(); | 220 | return &cci_pmu_models[probe_cci_revision()]; |
240 | |||
241 | return &port_event_range[rev]; | ||
242 | } | 221 | } |
243 | 222 | ||
244 | static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) | 223 | static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) |
@@ -816,9 +795,9 @@ static const struct attribute_group *pmu_attr_groups[] = { | |||
816 | 795 | ||
817 | static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) | 796 | static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) |
818 | { | 797 | { |
819 | char *name = pmu_names[probe_cci_revision()]; | 798 | char *name = cci_pmu->model->name; |
820 | cci_pmu->pmu = (struct pmu) { | 799 | cci_pmu->pmu = (struct pmu) { |
821 | .name = pmu_names[probe_cci_revision()], | 800 | .name = cci_pmu->model->name, |
822 | .task_ctx_nr = perf_invalid_context, | 801 | .task_ctx_nr = perf_invalid_context, |
823 | .pmu_enable = cci_pmu_enable, | 802 | .pmu_enable = cci_pmu_enable, |
824 | .pmu_disable = cci_pmu_disable, | 803 | .pmu_disable = cci_pmu_disable, |
@@ -871,6 +850,35 @@ static struct notifier_block cci_pmu_cpu_nb = { | |||
871 | .priority = CPU_PRI_PERF + 1, | 850 | .priority = CPU_PRI_PERF + 1, |
872 | }; | 851 | }; |
873 | 852 | ||
853 | static struct cci_pmu_model cci_pmu_models[] = { | ||
854 | [CCI_REV_R0] = { | ||
855 | .name = "CCI_400", | ||
856 | .event_ranges = { | ||
857 | [CCI_IF_SLAVE] = { | ||
858 | CCI_REV_R0_SLAVE_PORT_MIN_EV, | ||
859 | CCI_REV_R0_SLAVE_PORT_MAX_EV, | ||
860 | }, | ||
861 | [CCI_IF_MASTER] = { | ||
862 | CCI_REV_R0_MASTER_PORT_MIN_EV, | ||
863 | CCI_REV_R0_MASTER_PORT_MAX_EV, | ||
864 | }, | ||
865 | }, | ||
866 | }, | ||
867 | [CCI_REV_R1] = { | ||
868 | .name = "CCI_400_r1", | ||
869 | .event_ranges = { | ||
870 | [CCI_IF_SLAVE] = { | ||
871 | CCI_REV_R1_SLAVE_PORT_MIN_EV, | ||
872 | CCI_REV_R1_SLAVE_PORT_MAX_EV, | ||
873 | }, | ||
874 | [CCI_IF_MASTER] = { | ||
875 | CCI_REV_R1_MASTER_PORT_MIN_EV, | ||
876 | CCI_REV_R1_MASTER_PORT_MAX_EV, | ||
877 | }, | ||
878 | }, | ||
879 | }, | ||
880 | }; | ||
881 | |||
874 | static const struct of_device_id arm_cci_pmu_matches[] = { | 882 | static const struct of_device_id arm_cci_pmu_matches[] = { |
875 | { | 883 | { |
876 | .compatible = "arm,cci-400-pmu", | 884 | .compatible = "arm,cci-400-pmu", |
@@ -878,6 +886,16 @@ static const struct of_device_id arm_cci_pmu_matches[] = { | |||
878 | {}, | 886 | {}, |
879 | }; | 887 | }; |
880 | 888 | ||
889 | static inline const struct cci_pmu_model *get_cci_model(struct platform_device *pdev) | ||
890 | { | ||
891 | const struct of_device_id *match = of_match_node(arm_cci_pmu_matches, | ||
892 | pdev->dev.of_node); | ||
893 | if (!match) | ||
894 | return NULL; | ||
895 | |||
896 | return probe_cci_model(pdev); | ||
897 | } | ||
898 | |||
881 | static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) | 899 | static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs) |
882 | { | 900 | { |
883 | int i; | 901 | int i; |
@@ -893,11 +911,19 @@ static int cci_pmu_probe(struct platform_device *pdev) | |||
893 | { | 911 | { |
894 | struct resource *res; | 912 | struct resource *res; |
895 | int i, ret, irq; | 913 | int i, ret, irq; |
914 | const struct cci_pmu_model *model; | ||
915 | |||
916 | model = get_cci_model(pdev); | ||
917 | if (!model) { | ||
918 | dev_warn(&pdev->dev, "CCI PMU version not supported\n"); | ||
919 | return -ENODEV; | ||
920 | } | ||
896 | 921 | ||
897 | pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL); | 922 | pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL); |
898 | if (!pmu) | 923 | if (!pmu) |
899 | return -ENOMEM; | 924 | return -ENOMEM; |
900 | 925 | ||
926 | pmu->model = model; | ||
901 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 927 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
902 | pmu->base = devm_ioremap_resource(&pdev->dev, res); | 928 | pmu->base = devm_ioremap_resource(&pdev->dev, res); |
903 | if (IS_ERR(pmu->base)) | 929 | if (IS_ERR(pmu->base)) |
@@ -929,12 +955,6 @@ static int cci_pmu_probe(struct platform_device *pdev) | |||
929 | return -EINVAL; | 955 | return -EINVAL; |
930 | } | 956 | } |
931 | 957 | ||
932 | pmu->port_ranges = port_range_by_rev(); | ||
933 | if (!pmu->port_ranges) { | ||
934 | dev_warn(&pdev->dev, "CCI PMU version not supported\n"); | ||
935 | return -EINVAL; | ||
936 | } | ||
937 | |||
938 | raw_spin_lock_init(&pmu->hw_events.pmu_lock); | 958 | raw_spin_lock_init(&pmu->hw_events.pmu_lock); |
939 | mutex_init(&pmu->reserve_mutex); | 959 | mutex_init(&pmu->reserve_mutex); |
940 | atomic_set(&pmu->active_events, 0); | 960 | atomic_set(&pmu->active_events, 0); |
@@ -948,6 +968,7 @@ static int cci_pmu_probe(struct platform_device *pdev) | |||
948 | if (ret) | 968 | if (ret) |
949 | return ret; | 969 | return ret; |
950 | 970 | ||
971 | pr_info("ARM %s PMU driver probed", pmu->model->name); | ||
951 | return 0; | 972 | return 0; |
952 | } | 973 | } |
953 | 974 | ||