Commit | Line | Data |
---|---|---|
fcaf2036 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
9fbbe689 | 2 | /* |
55edcbb2 | 3 | * Copyright 2017 NXP |
e76bdfd7 | 4 | * Copyright 2011,2016 Freescale Semiconductor, Inc. |
9fbbe689 | 5 | * Copyright 2011 Linaro Ltd. |
9fbbe689 SG |
6 | */ |
7 | ||
9454a0ca | 8 | #include <linux/clk.h> |
e76bdfd7 | 9 | #include <linux/hrtimer.h> |
9fbbe689 | 10 | #include <linux/init.h> |
e76bdfd7 | 11 | #include <linux/interrupt.h> |
9fbbe689 SG |
12 | #include <linux/io.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/of_device.h> | |
e76bdfd7 ZS |
17 | #include <linux/perf_event.h> |
18 | #include <linux/slab.h> | |
9fbbe689 | 19 | |
666c8844 FE |
20 | #include "common.h" |
21 | ||
9fbbe689 SG |
22 | #define MMDC_MAPSR 0x404 |
23 | #define BP_MMDC_MAPSR_PSD 0 | |
24 | #define BP_MMDC_MAPSR_PSS 4 | |
25 | ||
ec336b28 AH |
26 | #define MMDC_MDMISC 0x18 |
27 | #define BM_MMDC_MDMISC_DDR_TYPE 0x18 | |
28 | #define BP_MMDC_MDMISC_DDR_TYPE 0x3 | |
29 | ||
e76bdfd7 ZS |
30 | #define TOTAL_CYCLES 0x0 |
31 | #define BUSY_CYCLES 0x1 | |
32 | #define READ_ACCESSES 0x2 | |
33 | #define WRITE_ACCESSES 0x3 | |
34 | #define READ_BYTES 0x4 | |
35 | #define WRITE_BYTES 0x5 | |
36 | ||
37 | /* Enables, resets, freezes, overflow profiling*/ | |
38 | #define DBG_DIS 0x0 | |
39 | #define DBG_EN 0x1 | |
40 | #define DBG_RST 0x2 | |
41 | #define PRF_FRZ 0x4 | |
42 | #define CYC_OVF 0x8 | |
c47c6fe4 | 43 | #define PROFILE_SEL 0x10 |
e76bdfd7 ZS |
44 | |
45 | #define MMDC_MADPCR0 0x410 | |
55edcbb2 | 46 | #define MMDC_MADPCR1 0x414 |
e76bdfd7 ZS |
47 | #define MMDC_MADPSR0 0x418 |
48 | #define MMDC_MADPSR1 0x41C | |
49 | #define MMDC_MADPSR2 0x420 | |
50 | #define MMDC_MADPSR3 0x424 | |
51 | #define MMDC_MADPSR4 0x428 | |
52 | #define MMDC_MADPSR5 0x42C | |
53 | ||
54 | #define MMDC_NUM_COUNTERS 6 | |
55 | ||
c47c6fe4 | 56 | #define MMDC_FLAG_PROFILE_SEL 0x1 |
55edcbb2 | 57 | #define MMDC_PRF_AXI_ID_CLEAR 0x0 |
c47c6fe4 | 58 | |
e76bdfd7 ZS |
59 | #define to_mmdc_pmu(p) container_of(p, struct mmdc_pmu, pmu) |
60 | ||
ec336b28 AH |
61 | static int ddr_type; |
62 | ||
c47c6fe4 FL |
63 | struct fsl_mmdc_devtype_data { |
64 | unsigned int flags; | |
65 | }; | |
66 | ||
67 | static const struct fsl_mmdc_devtype_data imx6q_data = { | |
68 | }; | |
69 | ||
70 | static const struct fsl_mmdc_devtype_data imx6qp_data = { | |
71 | .flags = MMDC_FLAG_PROFILE_SEL, | |
72 | }; | |
73 | ||
74 | static const struct of_device_id imx_mmdc_dt_ids[] = { | |
75 | { .compatible = "fsl,imx6q-mmdc", .data = (void *)&imx6q_data}, | |
76 | { .compatible = "fsl,imx6qp-mmdc", .data = (void *)&imx6qp_data}, | |
77 | { /* sentinel */ } | |
78 | }; | |
79 | ||
e76bdfd7 ZS |
80 | #ifdef CONFIG_PERF_EVENTS |
81 | ||
7f1931b3 | 82 | static enum cpuhp_state cpuhp_mmdc_state; |
e76bdfd7 ZS |
83 | static DEFINE_IDA(mmdc_ida); |
84 | ||
85 | PMU_EVENT_ATTR_STRING(total-cycles, mmdc_pmu_total_cycles, "event=0x00") | |
86 | PMU_EVENT_ATTR_STRING(busy-cycles, mmdc_pmu_busy_cycles, "event=0x01") | |
87 | PMU_EVENT_ATTR_STRING(read-accesses, mmdc_pmu_read_accesses, "event=0x02") | |
579616ba | 88 | PMU_EVENT_ATTR_STRING(write-accesses, mmdc_pmu_write_accesses, "event=0x03") |
e76bdfd7 ZS |
89 | PMU_EVENT_ATTR_STRING(read-bytes, mmdc_pmu_read_bytes, "event=0x04") |
90 | PMU_EVENT_ATTR_STRING(read-bytes.unit, mmdc_pmu_read_bytes_unit, "MB"); | |
91 | PMU_EVENT_ATTR_STRING(read-bytes.scale, mmdc_pmu_read_bytes_scale, "0.000001"); | |
92 | PMU_EVENT_ATTR_STRING(write-bytes, mmdc_pmu_write_bytes, "event=0x05") | |
93 | PMU_EVENT_ATTR_STRING(write-bytes.unit, mmdc_pmu_write_bytes_unit, "MB"); | |
94 | PMU_EVENT_ATTR_STRING(write-bytes.scale, mmdc_pmu_write_bytes_scale, "0.000001"); | |
95 | ||
96 | struct mmdc_pmu { | |
97 | struct pmu pmu; | |
98 | void __iomem *mmdc_base; | |
99 | cpumask_t cpu; | |
100 | struct hrtimer hrtimer; | |
101 | unsigned int active_events; | |
102 | struct device *dev; | |
103 | struct perf_event *mmdc_events[MMDC_NUM_COUNTERS]; | |
104 | struct hlist_node node; | |
c47c6fe4 | 105 | struct fsl_mmdc_devtype_data *devtype_data; |
e76bdfd7 ZS |
106 | }; |
107 | ||
108 | /* | |
109 | * Polling period is set to one second, overflow of total-cycles (the fastest | |
110 | * increasing counter) takes ten seconds so one second is safe | |
111 | */ | |
112 | static unsigned int mmdc_pmu_poll_period_us = 1000000; | |
113 | ||
114 | module_param_named(pmu_pmu_poll_period_us, mmdc_pmu_poll_period_us, uint, | |
115 | S_IRUGO | S_IWUSR); | |
116 | ||
117 | static ktime_t mmdc_pmu_timer_period(void) | |
118 | { | |
119 | return ns_to_ktime((u64)mmdc_pmu_poll_period_us * 1000); | |
120 | } | |
121 | ||
122 | static ssize_t mmdc_pmu_cpumask_show(struct device *dev, | |
123 | struct device_attribute *attr, char *buf) | |
124 | { | |
125 | struct mmdc_pmu *pmu_mmdc = dev_get_drvdata(dev); | |
126 | ||
127 | return cpumap_print_to_pagebuf(true, buf, &pmu_mmdc->cpu); | |
128 | } | |
129 | ||
130 | static struct device_attribute mmdc_pmu_cpumask_attr = | |
131 | __ATTR(cpumask, S_IRUGO, mmdc_pmu_cpumask_show, NULL); | |
132 | ||
133 | static struct attribute *mmdc_pmu_cpumask_attrs[] = { | |
134 | &mmdc_pmu_cpumask_attr.attr, | |
135 | NULL, | |
136 | }; | |
137 | ||
138 | static struct attribute_group mmdc_pmu_cpumask_attr_group = { | |
139 | .attrs = mmdc_pmu_cpumask_attrs, | |
140 | }; | |
141 | ||
142 | static struct attribute *mmdc_pmu_events_attrs[] = { | |
143 | &mmdc_pmu_total_cycles.attr.attr, | |
144 | &mmdc_pmu_busy_cycles.attr.attr, | |
145 | &mmdc_pmu_read_accesses.attr.attr, | |
146 | &mmdc_pmu_write_accesses.attr.attr, | |
147 | &mmdc_pmu_read_bytes.attr.attr, | |
148 | &mmdc_pmu_read_bytes_unit.attr.attr, | |
149 | &mmdc_pmu_read_bytes_scale.attr.attr, | |
150 | &mmdc_pmu_write_bytes.attr.attr, | |
151 | &mmdc_pmu_write_bytes_unit.attr.attr, | |
152 | &mmdc_pmu_write_bytes_scale.attr.attr, | |
153 | NULL, | |
154 | }; | |
155 | ||
156 | static struct attribute_group mmdc_pmu_events_attr_group = { | |
157 | .name = "events", | |
158 | .attrs = mmdc_pmu_events_attrs, | |
159 | }; | |
160 | ||
161 | PMU_FORMAT_ATTR(event, "config:0-63"); | |
55edcbb2 TB |
162 | PMU_FORMAT_ATTR(axi_id, "config1:0-63"); |
163 | ||
e76bdfd7 ZS |
164 | static struct attribute *mmdc_pmu_format_attrs[] = { |
165 | &format_attr_event.attr, | |
55edcbb2 | 166 | &format_attr_axi_id.attr, |
e76bdfd7 ZS |
167 | NULL, |
168 | }; | |
169 | ||
170 | static struct attribute_group mmdc_pmu_format_attr_group = { | |
171 | .name = "format", | |
172 | .attrs = mmdc_pmu_format_attrs, | |
173 | }; | |
174 | ||
175 | static const struct attribute_group *attr_groups[] = { | |
176 | &mmdc_pmu_events_attr_group, | |
177 | &mmdc_pmu_format_attr_group, | |
178 | &mmdc_pmu_cpumask_attr_group, | |
179 | NULL, | |
180 | }; | |
181 | ||
182 | static u32 mmdc_pmu_read_counter(struct mmdc_pmu *pmu_mmdc, int cfg) | |
183 | { | |
184 | void __iomem *mmdc_base, *reg; | |
185 | ||
186 | mmdc_base = pmu_mmdc->mmdc_base; | |
187 | ||
188 | switch (cfg) { | |
189 | case TOTAL_CYCLES: | |
190 | reg = mmdc_base + MMDC_MADPSR0; | |
191 | break; | |
192 | case BUSY_CYCLES: | |
193 | reg = mmdc_base + MMDC_MADPSR1; | |
194 | break; | |
195 | case READ_ACCESSES: | |
196 | reg = mmdc_base + MMDC_MADPSR2; | |
197 | break; | |
198 | case WRITE_ACCESSES: | |
199 | reg = mmdc_base + MMDC_MADPSR3; | |
200 | break; | |
201 | case READ_BYTES: | |
202 | reg = mmdc_base + MMDC_MADPSR4; | |
203 | break; | |
204 | case WRITE_BYTES: | |
205 | reg = mmdc_base + MMDC_MADPSR5; | |
206 | break; | |
207 | default: | |
208 | return WARN_ONCE(1, | |
209 | "invalid configuration %d for mmdc counter", cfg); | |
210 | } | |
211 | return readl(reg); | |
212 | } | |
213 | ||
214 | static int mmdc_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) | |
215 | { | |
216 | struct mmdc_pmu *pmu_mmdc = hlist_entry_safe(node, struct mmdc_pmu, node); | |
217 | int target; | |
218 | ||
219 | if (!cpumask_test_and_clear_cpu(cpu, &pmu_mmdc->cpu)) | |
220 | return 0; | |
221 | ||
222 | target = cpumask_any_but(cpu_online_mask, cpu); | |
223 | if (target >= nr_cpu_ids) | |
224 | return 0; | |
225 | ||
226 | perf_pmu_migrate_context(&pmu_mmdc->pmu, cpu, target); | |
227 | cpumask_set_cpu(target, &pmu_mmdc->cpu); | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | static bool mmdc_pmu_group_event_is_valid(struct perf_event *event, | |
233 | struct pmu *pmu, | |
234 | unsigned long *used_counters) | |
235 | { | |
236 | int cfg = event->attr.config; | |
237 | ||
238 | if (is_software_event(event)) | |
239 | return true; | |
240 | ||
241 | if (event->pmu != pmu) | |
242 | return false; | |
243 | ||
244 | return !test_and_set_bit(cfg, used_counters); | |
245 | } | |
246 | ||
247 | /* | |
248 | * Each event has a single fixed-purpose counter, so we can only have a | |
249 | * single active event for each at any point in time. Here we just check | |
250 | * for duplicates, and rely on mmdc_pmu_event_init to verify that the HW | |
251 | * event numbers are valid. | |
252 | */ | |
253 | static bool mmdc_pmu_group_is_valid(struct perf_event *event) | |
254 | { | |
255 | struct pmu *pmu = event->pmu; | |
256 | struct perf_event *leader = event->group_leader; | |
257 | struct perf_event *sibling; | |
258 | unsigned long counter_mask = 0; | |
259 | ||
260 | set_bit(leader->attr.config, &counter_mask); | |
261 | ||
262 | if (event != leader) { | |
263 | if (!mmdc_pmu_group_event_is_valid(event, pmu, &counter_mask)) | |
264 | return false; | |
265 | } | |
266 | ||
edb39592 | 267 | for_each_sibling_event(sibling, leader) { |
e76bdfd7 ZS |
268 | if (!mmdc_pmu_group_event_is_valid(sibling, pmu, &counter_mask)) |
269 | return false; | |
270 | } | |
271 | ||
272 | return true; | |
273 | } | |
274 | ||
275 | static int mmdc_pmu_event_init(struct perf_event *event) | |
276 | { | |
277 | struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu); | |
278 | int cfg = event->attr.config; | |
279 | ||
280 | if (event->attr.type != event->pmu->type) | |
281 | return -ENOENT; | |
282 | ||
283 | if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) | |
284 | return -EOPNOTSUPP; | |
285 | ||
286 | if (event->cpu < 0) { | |
287 | dev_warn(pmu_mmdc->dev, "Can't provide per-task data!\n"); | |
288 | return -EOPNOTSUPP; | |
289 | } | |
290 | ||
cafa780e | 291 | if (event->attr.sample_period) |
e76bdfd7 ZS |
292 | return -EINVAL; |
293 | ||
294 | if (cfg < 0 || cfg >= MMDC_NUM_COUNTERS) | |
295 | return -EINVAL; | |
296 | ||
297 | if (!mmdc_pmu_group_is_valid(event)) | |
298 | return -EINVAL; | |
299 | ||
300 | event->cpu = cpumask_first(&pmu_mmdc->cpu); | |
301 | return 0; | |
302 | } | |
303 | ||
304 | static void mmdc_pmu_event_update(struct perf_event *event) | |
305 | { | |
306 | struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu); | |
307 | struct hw_perf_event *hwc = &event->hw; | |
308 | u64 delta, prev_raw_count, new_raw_count; | |
309 | ||
310 | do { | |
311 | prev_raw_count = local64_read(&hwc->prev_count); | |
312 | new_raw_count = mmdc_pmu_read_counter(pmu_mmdc, | |
313 | event->attr.config); | |
314 | } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, | |
315 | new_raw_count) != prev_raw_count); | |
316 | ||
317 | delta = (new_raw_count - prev_raw_count) & 0xFFFFFFFF; | |
318 | ||
319 | local64_add(delta, &event->count); | |
320 | } | |
321 | ||
322 | static void mmdc_pmu_event_start(struct perf_event *event, int flags) | |
323 | { | |
324 | struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu); | |
325 | struct hw_perf_event *hwc = &event->hw; | |
326 | void __iomem *mmdc_base, *reg; | |
c47c6fe4 | 327 | u32 val; |
e76bdfd7 ZS |
328 | |
329 | mmdc_base = pmu_mmdc->mmdc_base; | |
330 | reg = mmdc_base + MMDC_MADPCR0; | |
331 | ||
332 | /* | |
333 | * hrtimer is required because mmdc does not provide an interrupt so | |
334 | * polling is necessary | |
335 | */ | |
336 | hrtimer_start(&pmu_mmdc->hrtimer, mmdc_pmu_timer_period(), | |
337 | HRTIMER_MODE_REL_PINNED); | |
338 | ||
339 | local64_set(&hwc->prev_count, 0); | |
340 | ||
341 | writel(DBG_RST, reg); | |
c47c6fe4 | 342 | |
55edcbb2 TB |
343 | /* |
344 | * Write the AXI id parameter to MADPCR1. | |
345 | */ | |
346 | val = event->attr.config1; | |
347 | reg = mmdc_base + MMDC_MADPCR1; | |
348 | writel(val, reg); | |
349 | ||
350 | reg = mmdc_base + MMDC_MADPCR0; | |
c47c6fe4 FL |
351 | val = DBG_EN; |
352 | if (pmu_mmdc->devtype_data->flags & MMDC_FLAG_PROFILE_SEL) | |
353 | val |= PROFILE_SEL; | |
354 | ||
355 | writel(val, reg); | |
e76bdfd7 ZS |
356 | } |
357 | ||
358 | static int mmdc_pmu_event_add(struct perf_event *event, int flags) | |
359 | { | |
360 | struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu); | |
361 | struct hw_perf_event *hwc = &event->hw; | |
362 | ||
363 | int cfg = event->attr.config; | |
364 | ||
365 | if (flags & PERF_EF_START) | |
366 | mmdc_pmu_event_start(event, flags); | |
367 | ||
368 | if (pmu_mmdc->mmdc_events[cfg] != NULL) | |
369 | return -EAGAIN; | |
370 | ||
371 | pmu_mmdc->mmdc_events[cfg] = event; | |
372 | pmu_mmdc->active_events++; | |
373 | ||
374 | local64_set(&hwc->prev_count, mmdc_pmu_read_counter(pmu_mmdc, cfg)); | |
375 | ||
376 | return 0; | |
377 | } | |
378 | ||
379 | static void mmdc_pmu_event_stop(struct perf_event *event, int flags) | |
380 | { | |
381 | struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu); | |
382 | void __iomem *mmdc_base, *reg; | |
383 | ||
384 | mmdc_base = pmu_mmdc->mmdc_base; | |
385 | reg = mmdc_base + MMDC_MADPCR0; | |
386 | ||
387 | writel(PRF_FRZ, reg); | |
55edcbb2 TB |
388 | |
389 | reg = mmdc_base + MMDC_MADPCR1; | |
390 | writel(MMDC_PRF_AXI_ID_CLEAR, reg); | |
391 | ||
e76bdfd7 ZS |
392 | mmdc_pmu_event_update(event); |
393 | } | |
394 | ||
395 | static void mmdc_pmu_event_del(struct perf_event *event, int flags) | |
396 | { | |
397 | struct mmdc_pmu *pmu_mmdc = to_mmdc_pmu(event->pmu); | |
398 | int cfg = event->attr.config; | |
399 | ||
400 | pmu_mmdc->mmdc_events[cfg] = NULL; | |
401 | pmu_mmdc->active_events--; | |
402 | ||
403 | if (pmu_mmdc->active_events == 0) | |
404 | hrtimer_cancel(&pmu_mmdc->hrtimer); | |
405 | ||
406 | mmdc_pmu_event_stop(event, PERF_EF_UPDATE); | |
407 | } | |
408 | ||
409 | static void mmdc_pmu_overflow_handler(struct mmdc_pmu *pmu_mmdc) | |
410 | { | |
411 | int i; | |
412 | ||
413 | for (i = 0; i < MMDC_NUM_COUNTERS; i++) { | |
414 | struct perf_event *event = pmu_mmdc->mmdc_events[i]; | |
415 | ||
416 | if (event) | |
417 | mmdc_pmu_event_update(event); | |
418 | } | |
419 | } | |
420 | ||
421 | static enum hrtimer_restart mmdc_pmu_timer_handler(struct hrtimer *hrtimer) | |
422 | { | |
423 | struct mmdc_pmu *pmu_mmdc = container_of(hrtimer, struct mmdc_pmu, | |
424 | hrtimer); | |
425 | ||
426 | mmdc_pmu_overflow_handler(pmu_mmdc); | |
427 | hrtimer_forward_now(hrtimer, mmdc_pmu_timer_period()); | |
428 | ||
429 | return HRTIMER_RESTART; | |
430 | } | |
431 | ||
432 | static int mmdc_pmu_init(struct mmdc_pmu *pmu_mmdc, | |
433 | void __iomem *mmdc_base, struct device *dev) | |
434 | { | |
435 | int mmdc_num; | |
436 | ||
437 | *pmu_mmdc = (struct mmdc_pmu) { | |
438 | .pmu = (struct pmu) { | |
439 | .task_ctx_nr = perf_invalid_context, | |
440 | .attr_groups = attr_groups, | |
441 | .event_init = mmdc_pmu_event_init, | |
442 | .add = mmdc_pmu_event_add, | |
443 | .del = mmdc_pmu_event_del, | |
444 | .start = mmdc_pmu_event_start, | |
445 | .stop = mmdc_pmu_event_stop, | |
446 | .read = mmdc_pmu_event_update, | |
cafa780e | 447 | .capabilities = PERF_PMU_CAP_NO_EXCLUDE, |
e76bdfd7 ZS |
448 | }, |
449 | .mmdc_base = mmdc_base, | |
450 | .dev = dev, | |
451 | .active_events = 0, | |
452 | }; | |
453 | ||
454 | mmdc_num = ida_simple_get(&mmdc_ida, 0, 0, GFP_KERNEL); | |
455 | ||
456 | return mmdc_num; | |
457 | } | |
458 | ||
459 | static int imx_mmdc_remove(struct platform_device *pdev) | |
460 | { | |
461 | struct mmdc_pmu *pmu_mmdc = platform_get_drvdata(pdev); | |
462 | ||
a051f220 | 463 | cpuhp_state_remove_instance_nocalls(cpuhp_mmdc_state, &pmu_mmdc->node); |
e76bdfd7 | 464 | perf_pmu_unregister(&pmu_mmdc->pmu); |
e76bdfd7 ZS |
465 | kfree(pmu_mmdc); |
466 | return 0; | |
467 | } | |
468 | ||
469 | static int imx_mmdc_perf_init(struct platform_device *pdev, void __iomem *mmdc_base) | |
470 | { | |
471 | struct mmdc_pmu *pmu_mmdc; | |
472 | char *name; | |
473 | int mmdc_num; | |
474 | int ret; | |
c47c6fe4 FL |
475 | const struct of_device_id *of_id = |
476 | of_match_device(imx_mmdc_dt_ids, &pdev->dev); | |
e76bdfd7 ZS |
477 | |
478 | pmu_mmdc = kzalloc(sizeof(*pmu_mmdc), GFP_KERNEL); | |
479 | if (!pmu_mmdc) { | |
480 | pr_err("failed to allocate PMU device!\n"); | |
481 | return -ENOMEM; | |
482 | } | |
483 | ||
a051f220 TG |
484 | /* The first instance registers the hotplug state */ |
485 | if (!cpuhp_mmdc_state) { | |
486 | ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, | |
487 | "perf/arm/mmdc:online", NULL, | |
488 | mmdc_pmu_offline_cpu); | |
489 | if (ret < 0) { | |
490 | pr_err("cpuhp_setup_state_multi failed\n"); | |
491 | goto pmu_free; | |
492 | } | |
493 | cpuhp_mmdc_state = ret; | |
494 | } | |
495 | ||
e76bdfd7 ZS |
496 | mmdc_num = mmdc_pmu_init(pmu_mmdc, mmdc_base, &pdev->dev); |
497 | if (mmdc_num == 0) | |
498 | name = "mmdc"; | |
499 | else | |
500 | name = devm_kasprintf(&pdev->dev, | |
501 | GFP_KERNEL, "mmdc%d", mmdc_num); | |
502 | ||
c47c6fe4 FL |
503 | pmu_mmdc->devtype_data = (struct fsl_mmdc_devtype_data *)of_id->data; |
504 | ||
e76bdfd7 ZS |
505 | hrtimer_init(&pmu_mmdc->hrtimer, CLOCK_MONOTONIC, |
506 | HRTIMER_MODE_REL); | |
507 | pmu_mmdc->hrtimer.function = mmdc_pmu_timer_handler; | |
508 | ||
a051f220 TG |
509 | cpumask_set_cpu(raw_smp_processor_id(), &pmu_mmdc->cpu); |
510 | ||
511 | /* Register the pmu instance for cpu hotplug */ | |
512 | cpuhp_state_add_instance_nocalls(cpuhp_mmdc_state, &pmu_mmdc->node); | |
e76bdfd7 ZS |
513 | |
514 | ret = perf_pmu_register(&(pmu_mmdc->pmu), name, -1); | |
e76bdfd7 ZS |
515 | if (ret) |
516 | goto pmu_register_err; | |
a051f220 TG |
517 | |
518 | platform_set_drvdata(pdev, pmu_mmdc); | |
e76bdfd7 ZS |
519 | return 0; |
520 | ||
521 | pmu_register_err: | |
522 | pr_warn("MMDC Perf PMU failed (%d), disabled\n", ret); | |
a051f220 | 523 | cpuhp_state_remove_instance_nocalls(cpuhp_mmdc_state, &pmu_mmdc->node); |
e76bdfd7 | 524 | hrtimer_cancel(&pmu_mmdc->hrtimer); |
a051f220 | 525 | pmu_free: |
e76bdfd7 ZS |
526 | kfree(pmu_mmdc); |
527 | return ret; | |
528 | } | |
529 | ||
530 | #else | |
531 | #define imx_mmdc_remove NULL | |
532 | #define imx_mmdc_perf_init(pdev, mmdc_base) 0 | |
533 | #endif | |
534 | ||
351a102d | 535 | static int imx_mmdc_probe(struct platform_device *pdev) |
9fbbe689 SG |
536 | { |
537 | struct device_node *np = pdev->dev.of_node; | |
538 | void __iomem *mmdc_base, *reg; | |
9454a0ca | 539 | struct clk *mmdc_ipg_clk; |
9fbbe689 | 540 | u32 val; |
9454a0ca AH |
541 | int err; |
542 | ||
543 | /* the ipg clock is optional */ | |
544 | mmdc_ipg_clk = devm_clk_get(&pdev->dev, NULL); | |
545 | if (IS_ERR(mmdc_ipg_clk)) | |
546 | mmdc_ipg_clk = NULL; | |
547 | ||
548 | err = clk_prepare_enable(mmdc_ipg_clk); | |
549 | if (err) { | |
550 | dev_err(&pdev->dev, "Unable to enable mmdc ipg clock.\n"); | |
551 | return err; | |
552 | } | |
9fbbe689 SG |
553 | |
554 | mmdc_base = of_iomap(np, 0); | |
555 | WARN_ON(!mmdc_base); | |
556 | ||
ec336b28 AH |
557 | reg = mmdc_base + MMDC_MDMISC; |
558 | /* Get ddr type */ | |
559 | val = readl_relaxed(reg); | |
560 | ddr_type = (val & BM_MMDC_MDMISC_DDR_TYPE) >> | |
561 | BP_MMDC_MDMISC_DDR_TYPE; | |
562 | ||
9fbbe689 SG |
563 | reg = mmdc_base + MMDC_MAPSR; |
564 | ||
565 | /* Enable automatic power saving */ | |
566 | val = readl_relaxed(reg); | |
567 | val &= ~(1 << BP_MMDC_MAPSR_PSD); | |
568 | writel_relaxed(val, reg); | |
569 | ||
e76bdfd7 | 570 | return imx_mmdc_perf_init(pdev, mmdc_base); |
9fbbe689 SG |
571 | } |
572 | ||
ec336b28 AH |
573 | int imx_mmdc_get_ddr_type(void) |
574 | { | |
575 | return ddr_type; | |
576 | } | |
577 | ||
9fbbe689 SG |
578 | static struct platform_driver imx_mmdc_driver = { |
579 | .driver = { | |
580 | .name = "imx-mmdc", | |
9fbbe689 SG |
581 | .of_match_table = imx_mmdc_dt_ids, |
582 | }, | |
583 | .probe = imx_mmdc_probe, | |
e76bdfd7 | 584 | .remove = imx_mmdc_remove, |
9fbbe689 SG |
585 | }; |
586 | ||
587 | static int __init imx_mmdc_init(void) | |
588 | { | |
589 | return platform_driver_register(&imx_mmdc_driver); | |
590 | } | |
591 | postcore_initcall(imx_mmdc_init); |