root/arch/arm/mach-imx/mmdc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PMU_EVENT_ATTR_STRING
  2. mmdc_pmu_timer_period
  3. mmdc_pmu_cpumask_show
  4. mmdc_pmu_read_counter
  5. mmdc_pmu_offline_cpu
  6. mmdc_pmu_group_event_is_valid
  7. mmdc_pmu_group_is_valid
  8. mmdc_pmu_event_init
  9. mmdc_pmu_event_update
  10. mmdc_pmu_event_start
  11. mmdc_pmu_event_add
  12. mmdc_pmu_event_stop
  13. mmdc_pmu_event_del
  14. mmdc_pmu_overflow_handler
  15. mmdc_pmu_timer_handler
  16. mmdc_pmu_init
  17. imx_mmdc_remove
  18. imx_mmdc_perf_init
  19. imx_mmdc_probe
  20. imx_mmdc_get_ddr_type
  21. imx_mmdc_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright 2017 NXP
   4  * Copyright 2011,2016 Freescale Semiconductor, Inc.
   5  * Copyright 2011 Linaro Ltd.
   6  */
   7 
   8 #include <linux/clk.h>
   9 #include <linux/hrtimer.h>
  10 #include <linux/init.h>
  11 #include <linux/interrupt.h>
  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>
  17 #include <linux/perf_event.h>
  18 #include <linux/slab.h>
  19 
  20 #include "common.h"
  21 
  22 #define MMDC_MAPSR              0x404
  23 #define BP_MMDC_MAPSR_PSD       0
  24 #define BP_MMDC_MAPSR_PSS       4
  25 
  26 #define MMDC_MDMISC             0x18
  27 #define BM_MMDC_MDMISC_DDR_TYPE 0x18
  28 #define BP_MMDC_MDMISC_DDR_TYPE 0x3
  29 
  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
  43 #define PROFILE_SEL             0x10
  44 
  45 #define MMDC_MADPCR0    0x410
  46 #define MMDC_MADPCR1    0x414
  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 
  56 #define MMDC_FLAG_PROFILE_SEL   0x1
  57 #define MMDC_PRF_AXI_ID_CLEAR   0x0
  58 
  59 #define to_mmdc_pmu(p) container_of(p, struct mmdc_pmu, pmu)
  60 
  61 static int ddr_type;
  62 
  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 
  80 #ifdef CONFIG_PERF_EVENTS
  81 
  82 static enum cpuhp_state cpuhp_mmdc_state;
  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")
  88 PMU_EVENT_ATTR_STRING(write-accesses, mmdc_pmu_write_accesses, "event=0x03")
  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;
 105         struct fsl_mmdc_devtype_data *devtype_data;
 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");
 162 PMU_FORMAT_ATTR(axi_id, "config1:0-63");
 163 
 164 static struct attribute *mmdc_pmu_format_attrs[] = {
 165         &format_attr_event.attr,
 166         &format_attr_axi_id.attr,
 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 
 267         for_each_sibling_event(sibling, leader) {
 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 
 291         if (event->attr.sample_period)
 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;
 327         u32 val;
 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);
 342 
 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;
 351         val = DBG_EN;
 352         if (pmu_mmdc->devtype_data->flags & MMDC_FLAG_PROFILE_SEL)
 353                 val |= PROFILE_SEL;
 354 
 355         writel(val, reg);
 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);
 388 
 389         reg = mmdc_base + MMDC_MADPCR1;
 390         writel(MMDC_PRF_AXI_ID_CLEAR, reg);
 391 
 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,
 447                         .capabilities   = PERF_PMU_CAP_NO_EXCLUDE,
 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 
 463         cpuhp_state_remove_instance_nocalls(cpuhp_mmdc_state, &pmu_mmdc->node);
 464         perf_pmu_unregister(&pmu_mmdc->pmu);
 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;
 475         const struct of_device_id *of_id =
 476                 of_match_device(imx_mmdc_dt_ids, &pdev->dev);
 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 
 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 
 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 
 503         pmu_mmdc->devtype_data = (struct fsl_mmdc_devtype_data *)of_id->data;
 504 
 505         hrtimer_init(&pmu_mmdc->hrtimer, CLOCK_MONOTONIC,
 506                         HRTIMER_MODE_REL);
 507         pmu_mmdc->hrtimer.function = mmdc_pmu_timer_handler;
 508 
 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);
 513 
 514         ret = perf_pmu_register(&(pmu_mmdc->pmu), name, -1);
 515         if (ret)
 516                 goto pmu_register_err;
 517 
 518         platform_set_drvdata(pdev, pmu_mmdc);
 519         return 0;
 520 
 521 pmu_register_err:
 522         pr_warn("MMDC Perf PMU failed (%d), disabled\n", ret);
 523         cpuhp_state_remove_instance_nocalls(cpuhp_mmdc_state, &pmu_mmdc->node);
 524         hrtimer_cancel(&pmu_mmdc->hrtimer);
 525 pmu_free:
 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 
 535 static int imx_mmdc_probe(struct platform_device *pdev)
 536 {
 537         struct device_node *np = pdev->dev.of_node;
 538         void __iomem *mmdc_base, *reg;
 539         struct clk *mmdc_ipg_clk;
 540         u32 val;
 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         }
 553 
 554         mmdc_base = of_iomap(np, 0);
 555         WARN_ON(!mmdc_base);
 556 
 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 
 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 
 570         return imx_mmdc_perf_init(pdev, mmdc_base);
 571 }
 572 
 573 int imx_mmdc_get_ddr_type(void)
 574 {
 575         return ddr_type;
 576 }
 577 
 578 static struct platform_driver imx_mmdc_driver = {
 579         .driver         = {
 580                 .name   = "imx-mmdc",
 581                 .of_match_table = imx_mmdc_dt_ids,
 582         },
 583         .probe          = imx_mmdc_probe,
 584         .remove         = imx_mmdc_remove,
 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);

/* [<][>][^][v][top][bottom][index][help] */