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