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 resource *res; 106 void __iomem *ddrctl; 107 struct device *dev; 108 u32 reg; 109 110 dev = &pdev->dev; 111 112 setting = da8xx_ddrctl_get_board_settings(); 113 if (!setting) { 114 dev_err(dev, "no settings defined for this board\n"); 115 return -EINVAL; 116 } 117 118 ddrctl = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 119 if (IS_ERR(ddrctl)) { 120 dev_err(dev, "unable to map memory controller registers\n"); 121 return PTR_ERR(ddrctl); 122 } 123 124 for (; setting->name; setting++) { 125 knob = da8xx_ddrctl_match_knob(setting); 126 if (!knob) { 127 dev_warn(dev, 128 "no such config option: %s\n", setting->name); 129 continue; 130 } 131 132 if (knob->reg + sizeof(u32) > resource_size(res)) { 133 dev_warn(dev, 134 "register offset of '%s' exceeds mapped memory size\n", 135 knob->name); 136 continue; 137 } 138 139 reg = readl(ddrctl + knob->reg); 140 reg &= knob->mask; 141 reg |= setting->val << knob->shift; 142 143 dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name); 144 145 writel(reg, ddrctl + knob->reg); 146 } 147 148 return 0; 149 } 150 151 static const struct of_device_id da8xx_ddrctl_of_match[] = { 152 { .compatible = "ti,da850-ddr-controller", }, 153 { }, 154 }; 155 156 static struct platform_driver da8xx_ddrctl_driver = { 157 .probe = da8xx_ddrctl_probe, 158 .driver = { 159 .name = "da850-ddr-controller", 160 .of_match_table = da8xx_ddrctl_of_match, 161 }, 162 }; 163 module_platform_driver(da8xx_ddrctl_driver); 164 165 MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); 166 MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver"); 167