xref: /openbmc/linux/drivers/memory/da8xx-ddrctl.c (revision 6d99a79c)
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