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/platform_device.h> 18 #include <linux/io.h> 19 20 /* 21 * REVISIT: Linux doesn't have a good framework for the kind of performance 22 * knobs this driver controls. We can't use device tree properties as it deals 23 * with hardware configuration rather than description. We also don't want to 24 * commit to maintaining some random sysfs attributes. 25 * 26 * For now we just hardcode the register values for the boards that need 27 * some changes (as is the case for the LCD controller on da850-lcdk - the 28 * first board we support here). When linux gets an appropriate framework, 29 * we'll easily convert the driver to it. 30 */ 31 32 struct da8xx_ddrctl_config_knob { 33 const char *name; 34 u32 reg; 35 u32 mask; 36 u32 shift; 37 }; 38 39 static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = { 40 { 41 .name = "da850-pbbpr", 42 .reg = 0x20, 43 .mask = 0xffffff00, 44 .shift = 0, 45 }, 46 }; 47 48 struct da8xx_ddrctl_setting { 49 const char *name; 50 u32 val; 51 }; 52 53 struct da8xx_ddrctl_board_settings { 54 const char *board; 55 const struct da8xx_ddrctl_setting *settings; 56 }; 57 58 static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = { 59 { 60 .name = "da850-pbbpr", 61 .val = 0x20, 62 }, 63 { } 64 }; 65 66 static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = { 67 { 68 .board = "ti,da850-lcdk", 69 .settings = da850_lcdk_ddrctl_settings, 70 }, 71 }; 72 73 static const struct da8xx_ddrctl_config_knob * 74 da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting) 75 { 76 const struct da8xx_ddrctl_config_knob *knob; 77 int i; 78 79 for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) { 80 knob = &da8xx_ddrctl_knobs[i]; 81 82 if (strcmp(knob->name, setting->name) == 0) 83 return knob; 84 } 85 86 return NULL; 87 } 88 89 static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void) 90 { 91 const struct da8xx_ddrctl_board_settings *board_settings; 92 int i; 93 94 for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) { 95 board_settings = &da8xx_ddrctl_board_confs[i]; 96 97 if (of_machine_is_compatible(board_settings->board)) 98 return board_settings->settings; 99 } 100 101 return NULL; 102 } 103 104 static int da8xx_ddrctl_probe(struct platform_device *pdev) 105 { 106 const struct da8xx_ddrctl_config_knob *knob; 107 const struct da8xx_ddrctl_setting *setting; 108 struct device_node *node; 109 struct resource *res; 110 void __iomem *ddrctl; 111 struct device *dev; 112 u32 reg; 113 114 dev = &pdev->dev; 115 node = dev->of_node; 116 117 setting = da8xx_ddrctl_get_board_settings(); 118 if (!setting) { 119 dev_err(dev, "no settings defined for this board\n"); 120 return -EINVAL; 121 } 122 123 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 124 ddrctl = devm_ioremap_resource(dev, res); 125 if (IS_ERR(ddrctl)) { 126 dev_err(dev, "unable to map memory controller registers\n"); 127 return PTR_ERR(ddrctl); 128 } 129 130 for (; setting->name; setting++) { 131 knob = da8xx_ddrctl_match_knob(setting); 132 if (!knob) { 133 dev_warn(dev, 134 "no such config option: %s\n", setting->name); 135 continue; 136 } 137 138 if (knob->reg + sizeof(u32) > resource_size(res)) { 139 dev_warn(dev, 140 "register offset of '%s' exceeds mapped memory size\n", 141 knob->name); 142 continue; 143 } 144 145 reg = readl(ddrctl + knob->reg); 146 reg &= knob->mask; 147 reg |= setting->val << knob->shift; 148 149 dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name); 150 151 writel(reg, ddrctl + knob->reg); 152 } 153 154 return 0; 155 } 156 157 static const struct of_device_id da8xx_ddrctl_of_match[] = { 158 { .compatible = "ti,da850-ddr-controller", }, 159 { }, 160 }; 161 162 static struct platform_driver da8xx_ddrctl_driver = { 163 .probe = da8xx_ddrctl_probe, 164 .driver = { 165 .name = "da850-ddr-controller", 166 .of_match_table = da8xx_ddrctl_of_match, 167 }, 168 }; 169 module_platform_driver(da8xx_ddrctl_driver); 170 171 MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); 172 MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver"); 173 MODULE_LICENSE("GPL v2"); 174