root/drivers/memory/da8xx-ddrctl.c

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

DEFINITIONS

This source file includes following definitions.
  1. da8xx_ddrctl_match_knob
  2. da8xx_ddrctl_get_board_settings
  3. da8xx_ddrctl_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * TI da8xx DDR2/mDDR controller driver
   4  *
   5  * Copyright (C) 2016 BayLibre SAS
   6  *
   7  * Author:
   8  *   Bartosz Golaszewski <bgolaszewski@baylibre.com>
   9  */
  10 
  11 #include <linux/module.h>
  12 #include <linux/of.h>
  13 #include <linux/of_device.h>
  14 #include <linux/platform_device.h>
  15 #include <linux/io.h>
  16 
  17 /*
  18  * REVISIT: Linux doesn't have a good framework for the kind of performance
  19  * knobs this driver controls. We can't use device tree properties as it deals
  20  * with hardware configuration rather than description. We also don't want to
  21  * commit to maintaining some random sysfs attributes.
  22  *
  23  * For now we just hardcode the register values for the boards that need
  24  * some changes (as is the case for the LCD controller on da850-lcdk - the
  25  * first board we support here). When linux gets an appropriate framework,
  26  * we'll easily convert the driver to it.
  27  */
  28 
  29 struct da8xx_ddrctl_config_knob {
  30         const char *name;
  31         u32 reg;
  32         u32 mask;
  33         u32 shift;
  34 };
  35 
  36 static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = {
  37         {
  38                 .name = "da850-pbbpr",
  39                 .reg = 0x20,
  40                 .mask = 0xffffff00,
  41                 .shift = 0,
  42         },
  43 };
  44 
  45 struct da8xx_ddrctl_setting {
  46         const char *name;
  47         u32 val;
  48 };
  49 
  50 struct da8xx_ddrctl_board_settings {
  51         const char *board;
  52         const struct da8xx_ddrctl_setting *settings;
  53 };
  54 
  55 static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = {
  56         {
  57                 .name = "da850-pbbpr",
  58                 .val = 0x20,
  59         },
  60         { }
  61 };
  62 
  63 static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = {
  64         {
  65                 .board = "ti,da850-lcdk",
  66                 .settings = da850_lcdk_ddrctl_settings,
  67         },
  68 };
  69 
  70 static const struct da8xx_ddrctl_config_knob *
  71 da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting)
  72 {
  73         const struct da8xx_ddrctl_config_knob *knob;
  74         int i;
  75 
  76         for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) {
  77                 knob = &da8xx_ddrctl_knobs[i];
  78 
  79                 if (strcmp(knob->name, setting->name) == 0)
  80                         return knob;
  81         }
  82 
  83         return NULL;
  84 }
  85 
  86 static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void)
  87 {
  88         const struct da8xx_ddrctl_board_settings *board_settings;
  89         int i;
  90 
  91         for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) {
  92                 board_settings = &da8xx_ddrctl_board_confs[i];
  93 
  94                 if (of_machine_is_compatible(board_settings->board))
  95                         return board_settings->settings;
  96         }
  97 
  98         return NULL;
  99 }
 100 
 101 static int da8xx_ddrctl_probe(struct platform_device *pdev)
 102 {
 103         const struct da8xx_ddrctl_config_knob *knob;
 104         const struct da8xx_ddrctl_setting *setting;
 105         struct device_node *node;
 106         struct resource *res;
 107         void __iomem *ddrctl;
 108         struct device *dev;
 109         u32 reg;
 110 
 111         dev = &pdev->dev;
 112         node = dev->of_node;
 113 
 114         setting = da8xx_ddrctl_get_board_settings();
 115         if (!setting) {
 116                 dev_err(dev, "no settings defined for this board\n");
 117                 return -EINVAL;
 118         }
 119 
 120         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 121         ddrctl = devm_ioremap_resource(dev, res);
 122         if (IS_ERR(ddrctl)) {
 123                 dev_err(dev, "unable to map memory controller registers\n");
 124                 return PTR_ERR(ddrctl);
 125         }
 126 
 127         for (; setting->name; setting++) {
 128                 knob = da8xx_ddrctl_match_knob(setting);
 129                 if (!knob) {
 130                         dev_warn(dev,
 131                                  "no such config option: %s\n", setting->name);
 132                         continue;
 133                 }
 134 
 135                 if (knob->reg + sizeof(u32) > resource_size(res)) {
 136                         dev_warn(dev,
 137                                  "register offset of '%s' exceeds mapped memory size\n",
 138                                  knob->name);
 139                         continue;
 140                 }
 141 
 142                 reg = readl(ddrctl + knob->reg);
 143                 reg &= knob->mask;
 144                 reg |= setting->val << knob->shift;
 145 
 146                 dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name);
 147 
 148                 writel(reg, ddrctl + knob->reg);
 149         }
 150 
 151         return 0;
 152 }
 153 
 154 static const struct of_device_id da8xx_ddrctl_of_match[] = {
 155         { .compatible = "ti,da850-ddr-controller", },
 156         { },
 157 };
 158 
 159 static struct platform_driver da8xx_ddrctl_driver = {
 160         .probe = da8xx_ddrctl_probe,
 161         .driver = {
 162                 .name = "da850-ddr-controller",
 163                 .of_match_table = da8xx_ddrctl_of_match,
 164         },
 165 };
 166 module_platform_driver(da8xx_ddrctl_driver);
 167 
 168 MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
 169 MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver");
 170 MODULE_LICENSE("GPL v2");

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