Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

memory: davinci: add support for da8xx DDR2/mDDR controller

Create a new driver for the da8xx DDR2/mDDR controller and implement
support for writing to the Peripheral Bus Burst Priority Register.

Reviewed-by: Kevin Hilman <khilman@baylibre.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
[nsekhar@ti.com: subject line adjustment]
Signed-off-by: Sekhar Nori <nsekhar@ti.com>

authored by

Bartosz Golaszewski and committed by
Sekhar Nori
62a8a739 1001354c

+204
+20
Documentation/devicetree/bindings/memory-controllers/ti-da8xx-ddrctl.txt
··· 1 + * Device tree bindings for Texas Instruments da8xx DDR2/mDDR memory controller 2 + 3 + The DDR2/mDDR memory controller present on Texas Instruments da8xx SoCs features 4 + a set of registers which allow to tweak the controller's behavior. 5 + 6 + Documentation: 7 + OMAP-L138 (DA850) - http://www.ti.com/lit/ug/spruh82c/spruh82c.pdf 8 + 9 + Required properties: 10 + 11 + - compatible: "ti,da850-ddr-controller" - for da850 SoC based boards 12 + - reg: a tuple containing the base address of the memory 13 + controller and the size of the memory area to map 14 + 15 + Example for da850 shown below. 16 + 17 + ddrctl { 18 + compatible = "ti,da850-ddr-controller"; 19 + reg = <0xb0000000 0xe8>; 20 + };
+8
drivers/memory/Kconfig
··· 134 134 mainly help enable/disable iommu and control the power domain and 135 135 clocks for each local arbiter. 136 136 137 + config DA8XX_DDRCTL 138 + bool "Texas Instruments da8xx DDR2/mDDR driver" 139 + depends on ARCH_DAVINCI_DA8XX 140 + help 141 + This driver is for the DDR2/mDDR Memory Controller present on 142 + Texas Instruments da8xx SoCs. It's used to tweak various memory 143 + controller configuration options. 144 + 137 145 source "drivers/memory/samsung/Kconfig" 138 146 source "drivers/memory/tegra/Kconfig" 139 147
+1
drivers/memory/Makefile
··· 17 17 obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o 18 18 obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o 19 19 obj-$(CONFIG_MTK_SMI) += mtk-smi.o 20 + obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o 20 21 21 22 obj-$(CONFIG_SAMSUNG_MC) += samsung/ 22 23 obj-$(CONFIG_TEGRA_MC) += tegra/
+175
drivers/memory/da8xx-ddrctl.c
··· 1 + /* 2 + * TI da8xx DDR2/mDDR controller driver 3 + * 4 + * Copyright (C) 2016 BayLibre SAS 5 + * 6 + * Author: 7 + * Bartosz Golaszewski <bgolaszewski@baylibre.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + */ 13 + 14 + #include <linux/module.h> 15 + #include <linux/of.h> 16 + #include <linux/of_device.h> 17 + #include <linux/of_fdt.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/io.h> 20 + 21 + /* 22 + * REVISIT: Linux doesn't have a good framework for the kind of performance 23 + * knobs this driver controls. We can't use device tree properties as it deals 24 + * with hardware configuration rather than description. We also don't want to 25 + * commit to maintaining some random sysfs attributes. 26 + * 27 + * For now we just hardcode the register values for the boards that need 28 + * some changes (as is the case for the LCD controller on da850-lcdk - the 29 + * first board we support here). When linux gets an appropriate framework, 30 + * we'll easily convert the driver to it. 31 + */ 32 + 33 + struct da8xx_ddrctl_config_knob { 34 + const char *name; 35 + u32 reg; 36 + u32 mask; 37 + u32 shift; 38 + }; 39 + 40 + static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = { 41 + { 42 + .name = "da850-pbbpr", 43 + .reg = 0x20, 44 + .mask = 0xffffff00, 45 + .shift = 0, 46 + }, 47 + }; 48 + 49 + struct da8xx_ddrctl_setting { 50 + const char *name; 51 + u32 val; 52 + }; 53 + 54 + struct da8xx_ddrctl_board_settings { 55 + const char *board; 56 + const struct da8xx_ddrctl_setting *settings; 57 + }; 58 + 59 + static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = { 60 + { 61 + .name = "da850-pbbpr", 62 + .val = 0x20, 63 + }, 64 + { } 65 + }; 66 + 67 + static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = { 68 + { 69 + .board = "ti,da850-lcdk", 70 + .settings = da850_lcdk_ddrctl_settings, 71 + }, 72 + }; 73 + 74 + static const struct da8xx_ddrctl_config_knob * 75 + da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting) 76 + { 77 + const struct da8xx_ddrctl_config_knob *knob; 78 + int i; 79 + 80 + for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) { 81 + knob = &da8xx_ddrctl_knobs[i]; 82 + 83 + if (strcmp(knob->name, setting->name) == 0) 84 + return knob; 85 + } 86 + 87 + return NULL; 88 + } 89 + 90 + static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void) 91 + { 92 + const struct da8xx_ddrctl_board_settings *board_settings; 93 + int i; 94 + 95 + for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) { 96 + board_settings = &da8xx_ddrctl_board_confs[i]; 97 + 98 + if (of_machine_is_compatible(board_settings->board)) 99 + return board_settings->settings; 100 + } 101 + 102 + return NULL; 103 + } 104 + 105 + static int da8xx_ddrctl_probe(struct platform_device *pdev) 106 + { 107 + const struct da8xx_ddrctl_config_knob *knob; 108 + const struct da8xx_ddrctl_setting *setting; 109 + struct device_node *node; 110 + struct resource *res; 111 + void __iomem *ddrctl; 112 + struct device *dev; 113 + u32 reg; 114 + 115 + dev = &pdev->dev; 116 + node = dev->of_node; 117 + 118 + setting = da8xx_ddrctl_get_board_settings(); 119 + if (!setting) { 120 + dev_err(dev, "no settings for board '%s'\n", 121 + of_flat_dt_get_machine_name()); 122 + return -EINVAL; 123 + } 124 + 125 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 126 + ddrctl = devm_ioremap_resource(dev, res); 127 + if (IS_ERR(ddrctl)) { 128 + dev_err(dev, "unable to map memory controller registers\n"); 129 + return PTR_ERR(ddrctl); 130 + } 131 + 132 + for (; setting->name; setting++) { 133 + knob = da8xx_ddrctl_match_knob(setting); 134 + if (!knob) { 135 + dev_warn(dev, 136 + "no such config option: %s\n", setting->name); 137 + continue; 138 + } 139 + 140 + if (knob->reg + sizeof(u32) > resource_size(res)) { 141 + dev_warn(dev, 142 + "register offset of '%s' exceeds mapped memory size\n", 143 + knob->name); 144 + continue; 145 + } 146 + 147 + reg = readl(ddrctl + knob->reg); 148 + reg &= knob->mask; 149 + reg |= setting->val << knob->shift; 150 + 151 + dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name); 152 + 153 + writel(reg, ddrctl + knob->reg); 154 + } 155 + 156 + return 0; 157 + } 158 + 159 + static const struct of_device_id da8xx_ddrctl_of_match[] = { 160 + { .compatible = "ti,da850-ddr-controller", }, 161 + { }, 162 + }; 163 + 164 + static struct platform_driver da8xx_ddrctl_driver = { 165 + .probe = da8xx_ddrctl_probe, 166 + .driver = { 167 + .name = "da850-ddr-controller", 168 + .of_match_table = da8xx_ddrctl_of_match, 169 + }, 170 + }; 171 + module_platform_driver(da8xx_ddrctl_driver); 172 + 173 + MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); 174 + MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver"); 175 + MODULE_LICENSE("GPL v2");