Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TI da8xx DDR2/mDDR controller driver
0004  *
0005  * Copyright (C) 2016 BayLibre SAS
0006  *
0007  * Author:
0008  *   Bartosz Golaszewski <bgolaszewski@baylibre.com>
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/io.h>
0016 
0017 /*
0018  * REVISIT: Linux doesn't have a good framework for the kind of performance
0019  * knobs this driver controls. We can't use device tree properties as it deals
0020  * with hardware configuration rather than description. We also don't want to
0021  * commit to maintaining some random sysfs attributes.
0022  *
0023  * For now we just hardcode the register values for the boards that need
0024  * some changes (as is the case for the LCD controller on da850-lcdk - the
0025  * first board we support here). When linux gets an appropriate framework,
0026  * we'll easily convert the driver to it.
0027  */
0028 
0029 struct da8xx_ddrctl_config_knob {
0030     const char *name;
0031     u32 reg;
0032     u32 mask;
0033     u32 shift;
0034 };
0035 
0036 static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = {
0037     {
0038         .name = "da850-pbbpr",
0039         .reg = 0x20,
0040         .mask = 0xffffff00,
0041         .shift = 0,
0042     },
0043 };
0044 
0045 struct da8xx_ddrctl_setting {
0046     const char *name;
0047     u32 val;
0048 };
0049 
0050 struct da8xx_ddrctl_board_settings {
0051     const char *board;
0052     const struct da8xx_ddrctl_setting *settings;
0053 };
0054 
0055 static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = {
0056     {
0057         .name = "da850-pbbpr",
0058         .val = 0x20,
0059     },
0060     { }
0061 };
0062 
0063 static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = {
0064     {
0065         .board = "ti,da850-lcdk",
0066         .settings = da850_lcdk_ddrctl_settings,
0067     },
0068 };
0069 
0070 static const struct da8xx_ddrctl_config_knob *
0071 da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting)
0072 {
0073     const struct da8xx_ddrctl_config_knob *knob;
0074     int i;
0075 
0076     for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) {
0077         knob = &da8xx_ddrctl_knobs[i];
0078 
0079         if (strcmp(knob->name, setting->name) == 0)
0080             return knob;
0081     }
0082 
0083     return NULL;
0084 }
0085 
0086 static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void)
0087 {
0088     const struct da8xx_ddrctl_board_settings *board_settings;
0089     int i;
0090 
0091     for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) {
0092         board_settings = &da8xx_ddrctl_board_confs[i];
0093 
0094         if (of_machine_is_compatible(board_settings->board))
0095             return board_settings->settings;
0096     }
0097 
0098     return NULL;
0099 }
0100 
0101 static int da8xx_ddrctl_probe(struct platform_device *pdev)
0102 {
0103     const struct da8xx_ddrctl_config_knob *knob;
0104     const struct da8xx_ddrctl_setting *setting;
0105     struct resource *res;
0106     void __iomem *ddrctl;
0107     struct device *dev;
0108     u32 reg;
0109 
0110     dev = &pdev->dev;
0111 
0112     setting = da8xx_ddrctl_get_board_settings();
0113     if (!setting) {
0114         dev_err(dev, "no settings defined for this board\n");
0115         return -EINVAL;
0116     }
0117 
0118     ddrctl = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0119     if (IS_ERR(ddrctl)) {
0120         dev_err(dev, "unable to map memory controller registers\n");
0121         return PTR_ERR(ddrctl);
0122     }
0123 
0124     for (; setting->name; setting++) {
0125         knob = da8xx_ddrctl_match_knob(setting);
0126         if (!knob) {
0127             dev_warn(dev,
0128                  "no such config option: %s\n", setting->name);
0129             continue;
0130         }
0131 
0132         if (knob->reg + sizeof(u32) > resource_size(res)) {
0133             dev_warn(dev,
0134                  "register offset of '%s' exceeds mapped memory size\n",
0135                  knob->name);
0136             continue;
0137         }
0138 
0139         reg = readl(ddrctl + knob->reg);
0140         reg &= knob->mask;
0141         reg |= setting->val << knob->shift;
0142 
0143         dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name);
0144 
0145         writel(reg, ddrctl + knob->reg);
0146     }
0147 
0148     return 0;
0149 }
0150 
0151 static const struct of_device_id da8xx_ddrctl_of_match[] = {
0152     { .compatible = "ti,da850-ddr-controller", },
0153     { },
0154 };
0155 
0156 static struct platform_driver da8xx_ddrctl_driver = {
0157     .probe = da8xx_ddrctl_probe,
0158     .driver = {
0159         .name = "da850-ddr-controller",
0160         .of_match_table = da8xx_ddrctl_of_match,
0161     },
0162 };
0163 module_platform_driver(da8xx_ddrctl_driver);
0164 
0165 MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
0166 MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver");
0167 MODULE_LICENSE("GPL v2");