1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
262a8a739SBartosz Golaszewski /*
362a8a739SBartosz Golaszewski * TI da8xx DDR2/mDDR controller driver
462a8a739SBartosz Golaszewski *
562a8a739SBartosz Golaszewski * Copyright (C) 2016 BayLibre SAS
662a8a739SBartosz Golaszewski *
762a8a739SBartosz Golaszewski * Author:
862a8a739SBartosz Golaszewski * Bartosz Golaszewski <bgolaszewski@baylibre.com>
962a8a739SBartosz Golaszewski */
1062a8a739SBartosz Golaszewski
1162a8a739SBartosz Golaszewski #include <linux/module.h>
1262a8a739SBartosz Golaszewski #include <linux/of.h>
1362a8a739SBartosz Golaszewski #include <linux/platform_device.h>
1462a8a739SBartosz Golaszewski #include <linux/io.h>
1562a8a739SBartosz Golaszewski
1662a8a739SBartosz Golaszewski /*
1762a8a739SBartosz Golaszewski * REVISIT: Linux doesn't have a good framework for the kind of performance
1862a8a739SBartosz Golaszewski * knobs this driver controls. We can't use device tree properties as it deals
1962a8a739SBartosz Golaszewski * with hardware configuration rather than description. We also don't want to
2062a8a739SBartosz Golaszewski * commit to maintaining some random sysfs attributes.
2162a8a739SBartosz Golaszewski *
2262a8a739SBartosz Golaszewski * For now we just hardcode the register values for the boards that need
2362a8a739SBartosz Golaszewski * some changes (as is the case for the LCD controller on da850-lcdk - the
2462a8a739SBartosz Golaszewski * first board we support here). When linux gets an appropriate framework,
2562a8a739SBartosz Golaszewski * we'll easily convert the driver to it.
2662a8a739SBartosz Golaszewski */
2762a8a739SBartosz Golaszewski
2862a8a739SBartosz Golaszewski struct da8xx_ddrctl_config_knob {
2962a8a739SBartosz Golaszewski const char *name;
3062a8a739SBartosz Golaszewski u32 reg;
3162a8a739SBartosz Golaszewski u32 mask;
3262a8a739SBartosz Golaszewski u32 shift;
3362a8a739SBartosz Golaszewski };
3462a8a739SBartosz Golaszewski
3562a8a739SBartosz Golaszewski static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = {
3662a8a739SBartosz Golaszewski {
3762a8a739SBartosz Golaszewski .name = "da850-pbbpr",
3862a8a739SBartosz Golaszewski .reg = 0x20,
3962a8a739SBartosz Golaszewski .mask = 0xffffff00,
4062a8a739SBartosz Golaszewski .shift = 0,
4162a8a739SBartosz Golaszewski },
4262a8a739SBartosz Golaszewski };
4362a8a739SBartosz Golaszewski
4462a8a739SBartosz Golaszewski struct da8xx_ddrctl_setting {
4562a8a739SBartosz Golaszewski const char *name;
4662a8a739SBartosz Golaszewski u32 val;
4762a8a739SBartosz Golaszewski };
4862a8a739SBartosz Golaszewski
4962a8a739SBartosz Golaszewski struct da8xx_ddrctl_board_settings {
5062a8a739SBartosz Golaszewski const char *board;
5162a8a739SBartosz Golaszewski const struct da8xx_ddrctl_setting *settings;
5262a8a739SBartosz Golaszewski };
5362a8a739SBartosz Golaszewski
5462a8a739SBartosz Golaszewski static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = {
5562a8a739SBartosz Golaszewski {
5662a8a739SBartosz Golaszewski .name = "da850-pbbpr",
5762a8a739SBartosz Golaszewski .val = 0x20,
5862a8a739SBartosz Golaszewski },
5962a8a739SBartosz Golaszewski { }
6062a8a739SBartosz Golaszewski };
6162a8a739SBartosz Golaszewski
6262a8a739SBartosz Golaszewski static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = {
6362a8a739SBartosz Golaszewski {
6462a8a739SBartosz Golaszewski .board = "ti,da850-lcdk",
6562a8a739SBartosz Golaszewski .settings = da850_lcdk_ddrctl_settings,
6662a8a739SBartosz Golaszewski },
6762a8a739SBartosz Golaszewski };
6862a8a739SBartosz Golaszewski
6962a8a739SBartosz Golaszewski static const struct da8xx_ddrctl_config_knob *
da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting * setting)7062a8a739SBartosz Golaszewski da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting)
7162a8a739SBartosz Golaszewski {
7262a8a739SBartosz Golaszewski const struct da8xx_ddrctl_config_knob *knob;
7362a8a739SBartosz Golaszewski int i;
7462a8a739SBartosz Golaszewski
7562a8a739SBartosz Golaszewski for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) {
7662a8a739SBartosz Golaszewski knob = &da8xx_ddrctl_knobs[i];
7762a8a739SBartosz Golaszewski
7862a8a739SBartosz Golaszewski if (strcmp(knob->name, setting->name) == 0)
7962a8a739SBartosz Golaszewski return knob;
8062a8a739SBartosz Golaszewski }
8162a8a739SBartosz Golaszewski
8262a8a739SBartosz Golaszewski return NULL;
8362a8a739SBartosz Golaszewski }
8462a8a739SBartosz Golaszewski
da8xx_ddrctl_get_board_settings(void)8562a8a739SBartosz Golaszewski static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void)
8662a8a739SBartosz Golaszewski {
8762a8a739SBartosz Golaszewski const struct da8xx_ddrctl_board_settings *board_settings;
8862a8a739SBartosz Golaszewski int i;
8962a8a739SBartosz Golaszewski
9062a8a739SBartosz Golaszewski for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) {
9162a8a739SBartosz Golaszewski board_settings = &da8xx_ddrctl_board_confs[i];
9262a8a739SBartosz Golaszewski
9362a8a739SBartosz Golaszewski if (of_machine_is_compatible(board_settings->board))
9462a8a739SBartosz Golaszewski return board_settings->settings;
9562a8a739SBartosz Golaszewski }
9662a8a739SBartosz Golaszewski
9762a8a739SBartosz Golaszewski return NULL;
9862a8a739SBartosz Golaszewski }
9962a8a739SBartosz Golaszewski
da8xx_ddrctl_probe(struct platform_device * pdev)10062a8a739SBartosz Golaszewski static int da8xx_ddrctl_probe(struct platform_device *pdev)
10162a8a739SBartosz Golaszewski {
10262a8a739SBartosz Golaszewski const struct da8xx_ddrctl_config_knob *knob;
10362a8a739SBartosz Golaszewski const struct da8xx_ddrctl_setting *setting;
10462a8a739SBartosz Golaszewski struct resource *res;
10562a8a739SBartosz Golaszewski void __iomem *ddrctl;
10662a8a739SBartosz Golaszewski struct device *dev;
10762a8a739SBartosz Golaszewski u32 reg;
10862a8a739SBartosz Golaszewski
10962a8a739SBartosz Golaszewski dev = &pdev->dev;
11062a8a739SBartosz Golaszewski
11162a8a739SBartosz Golaszewski setting = da8xx_ddrctl_get_board_settings();
11262a8a739SBartosz Golaszewski if (!setting) {
113d0c7546fSBartosz Golaszewski dev_err(dev, "no settings defined for this board\n");
11462a8a739SBartosz Golaszewski return -EINVAL;
11562a8a739SBartosz Golaszewski }
11662a8a739SBartosz Golaszewski
117*933713f5SKrzysztof Kozlowski ddrctl = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
11862a8a739SBartosz Golaszewski if (IS_ERR(ddrctl)) {
11962a8a739SBartosz Golaszewski dev_err(dev, "unable to map memory controller registers\n");
12062a8a739SBartosz Golaszewski return PTR_ERR(ddrctl);
12162a8a739SBartosz Golaszewski }
12262a8a739SBartosz Golaszewski
12362a8a739SBartosz Golaszewski for (; setting->name; setting++) {
12462a8a739SBartosz Golaszewski knob = da8xx_ddrctl_match_knob(setting);
12562a8a739SBartosz Golaszewski if (!knob) {
12662a8a739SBartosz Golaszewski dev_warn(dev,
12762a8a739SBartosz Golaszewski "no such config option: %s\n", setting->name);
12862a8a739SBartosz Golaszewski continue;
12962a8a739SBartosz Golaszewski }
13062a8a739SBartosz Golaszewski
13162a8a739SBartosz Golaszewski if (knob->reg + sizeof(u32) > resource_size(res)) {
13262a8a739SBartosz Golaszewski dev_warn(dev,
13362a8a739SBartosz Golaszewski "register offset of '%s' exceeds mapped memory size\n",
13462a8a739SBartosz Golaszewski knob->name);
13562a8a739SBartosz Golaszewski continue;
13662a8a739SBartosz Golaszewski }
13762a8a739SBartosz Golaszewski
13862a8a739SBartosz Golaszewski reg = readl(ddrctl + knob->reg);
13962a8a739SBartosz Golaszewski reg &= knob->mask;
14062a8a739SBartosz Golaszewski reg |= setting->val << knob->shift;
14162a8a739SBartosz Golaszewski
14262a8a739SBartosz Golaszewski dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name);
14362a8a739SBartosz Golaszewski
14462a8a739SBartosz Golaszewski writel(reg, ddrctl + knob->reg);
14562a8a739SBartosz Golaszewski }
14662a8a739SBartosz Golaszewski
14762a8a739SBartosz Golaszewski return 0;
14862a8a739SBartosz Golaszewski }
14962a8a739SBartosz Golaszewski
15062a8a739SBartosz Golaszewski static const struct of_device_id da8xx_ddrctl_of_match[] = {
15162a8a739SBartosz Golaszewski { .compatible = "ti,da850-ddr-controller", },
15262a8a739SBartosz Golaszewski { },
15362a8a739SBartosz Golaszewski };
15462a8a739SBartosz Golaszewski
15562a8a739SBartosz Golaszewski static struct platform_driver da8xx_ddrctl_driver = {
15662a8a739SBartosz Golaszewski .probe = da8xx_ddrctl_probe,
15762a8a739SBartosz Golaszewski .driver = {
15862a8a739SBartosz Golaszewski .name = "da850-ddr-controller",
15962a8a739SBartosz Golaszewski .of_match_table = da8xx_ddrctl_of_match,
16062a8a739SBartosz Golaszewski },
16162a8a739SBartosz Golaszewski };
16262a8a739SBartosz Golaszewski module_platform_driver(da8xx_ddrctl_driver);
16362a8a739SBartosz Golaszewski
16462a8a739SBartosz Golaszewski MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
16562a8a739SBartosz Golaszewski MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver");
166