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