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"); 176