aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/memory
diff options
context:
space:
mode:
authorEzequiel Garcia <ezequiel.garcia@free-electrons.com>2013-04-23 15:21:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-05-21 13:11:56 -0400
commit3edad321b1bd2e6c8b5f38146c115c8982438f06 (patch)
treed58e2393d49e1f1af0797778d1ed87fb6780e8dc /drivers/memory
parentced9017a4fc7ecf35a9c0c0bd8e46d14876b9fd1 (diff)
drivers: memory: Introduce Marvell EBU Device Bus driver
Marvell EBU SoCs such as Armada 370/XP, Orion5x (88f5xxx) and Discovery (mv78xx0) supports a Device Bus controller to access several kinds of memories and I/O devices (NOR, NAND, SRAM, FPGA). This commit adds a driver to handle this controller. So far only Armada 370, Armada XP and Discovery SoCs are supported. The driver must be registered through a device tree node; as explained in the binding document. For each child node in the device tree, this driver will: * set timing parameters * register a child device * setup an address decoding window, using the mbus driver Keep in mind the address decoding window setup is only a temporary hack. This code will be removed from this devbus driver as soon as a proper device tree binding for the mbus driver is added. Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Jason Cooper <jason@lakedaemon.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/memory')
-rw-r--r--drivers/memory/Kconfig10
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/mvebu-devbus.c340
3 files changed, 351 insertions, 0 deletions
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 067f31174a0e..29a11db365bc 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -20,6 +20,16 @@ config TI_EMIF
20 parameters and other settings during frequency, voltage and 20 parameters and other settings during frequency, voltage and
21 temperature changes 21 temperature changes
22 22
23config MVEBU_DEVBUS
24 bool "Marvell EBU Device Bus Controller"
25 default y
26 depends on PLAT_ORION && OF
27 help
28 This driver is for the Device Bus controller available in some
29 Marvell EBU SoCs such as Discovery (mv78xx0), Orion (88f5xxx) and
30 Armada 370 and Armada XP. This controller allows to handle flash
31 devices such as NOR, NAND, SRAM, and FPGA.
32
23config TEGRA20_MC 33config TEGRA20_MC
24 bool "Tegra20 Memory Controller(MC) driver" 34 bool "Tegra20 Memory Controller(MC) driver"
25 default y 35 default y
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 9cce5d70ed52..969d923dad93 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -6,5 +6,6 @@ ifeq ($(CONFIG_DDR),y)
6obj-$(CONFIG_OF) += of_memory.o 6obj-$(CONFIG_OF) += of_memory.o
7endif 7endif
8obj-$(CONFIG_TI_EMIF) += emif.o 8obj-$(CONFIG_TI_EMIF) += emif.o
9obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
9obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o 10obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
10obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o 11obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c
new file mode 100644
index 000000000000..978e8e3abc5c
--- /dev/null
+++ b/drivers/memory/mvebu-devbus.c
@@ -0,0 +1,340 @@
1/*
2 * Marvell EBU SoC Device Bus Controller
3 * (memory controller for NOR/NAND/SRAM/FPGA devices)
4 *
5 * Copyright (C) 2013 Marvell
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/slab.h>
24#include <linux/err.h>
25#include <linux/io.h>
26#include <linux/clk.h>
27#include <linux/mbus.h>
28#include <linux/of_platform.h>
29#include <linux/of_address.h>
30#include <linux/platform_device.h>
31
32/* Register definitions */
33#define DEV_WIDTH_BIT 30
34#define BADR_SKEW_BIT 28
35#define RD_HOLD_BIT 23
36#define ACC_NEXT_BIT 17
37#define RD_SETUP_BIT 12
38#define ACC_FIRST_BIT 6
39
40#define SYNC_ENABLE_BIT 24
41#define WR_HIGH_BIT 16
42#define WR_LOW_BIT 8
43
44#define READ_PARAM_OFFSET 0x0
45#define WRITE_PARAM_OFFSET 0x4
46
47static const char * const devbus_wins[] = {
48 "devbus-boot",
49 "devbus-cs0",
50 "devbus-cs1",
51 "devbus-cs2",
52 "devbus-cs3",
53};
54
55struct devbus_read_params {
56 u32 bus_width;
57 u32 badr_skew;
58 u32 turn_off;
59 u32 acc_first;
60 u32 acc_next;
61 u32 rd_setup;
62 u32 rd_hold;
63};
64
65struct devbus_write_params {
66 u32 sync_enable;
67 u32 wr_high;
68 u32 wr_low;
69 u32 ale_wr;
70};
71
72struct devbus {
73 struct device *dev;
74 void __iomem *base;
75 unsigned long tick_ps;
76};
77
78static int get_timing_param_ps(struct devbus *devbus,
79 struct device_node *node,
80 const char *name,
81 u32 *ticks)
82{
83 u32 time_ps;
84 int err;
85
86 err = of_property_read_u32(node, name, &time_ps);
87 if (err < 0) {
88 dev_err(devbus->dev, "%s has no '%s' property\n",
89 name, node->full_name);
90 return err;
91 }
92
93 *ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps;
94
95 dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n",
96 name, time_ps, *ticks);
97 return 0;
98}
99
100static int devbus_set_timing_params(struct devbus *devbus,
101 struct device_node *node)
102{
103 struct devbus_read_params r;
104 struct devbus_write_params w;
105 u32 value;
106 int err;
107
108 dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n",
109 devbus->tick_ps);
110
111 /* Get read timings */
112 err = of_property_read_u32(node, "devbus,bus-width", &r.bus_width);
113 if (err < 0) {
114 dev_err(devbus->dev,
115 "%s has no 'devbus,bus-width' property\n",
116 node->full_name);
117 return err;
118 }
119 /* Convert bit width to byte width */
120 r.bus_width /= 8;
121
122 err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
123 &r.badr_skew);
124 if (err < 0)
125 return err;
126
127 err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps",
128 &r.turn_off);
129 if (err < 0)
130 return err;
131
132 err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps",
133 &r.acc_first);
134 if (err < 0)
135 return err;
136
137 err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps",
138 &r.acc_next);
139 if (err < 0)
140 return err;
141
142 err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps",
143 &r.rd_setup);
144 if (err < 0)
145 return err;
146
147 err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps",
148 &r.rd_hold);
149 if (err < 0)
150 return err;
151
152 /* Get write timings */
153 err = of_property_read_u32(node, "devbus,sync-enable",
154 &w.sync_enable);
155 if (err < 0) {
156 dev_err(devbus->dev,
157 "%s has no 'devbus,sync-enable' property\n",
158 node->full_name);
159 return err;
160 }
161
162 err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps",
163 &w.ale_wr);
164 if (err < 0)
165 return err;
166
167 err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps",
168 &w.wr_low);
169 if (err < 0)
170 return err;
171
172 err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps",
173 &w.wr_high);
174 if (err < 0)
175 return err;
176
177 /* Set read timings */
178 value = r.bus_width << DEV_WIDTH_BIT |
179 r.badr_skew << BADR_SKEW_BIT |
180 r.rd_hold << RD_HOLD_BIT |
181 r.acc_next << ACC_NEXT_BIT |
182 r.rd_setup << RD_SETUP_BIT |
183 r.acc_first << ACC_FIRST_BIT |
184 r.turn_off;
185
186 dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n",
187 devbus->base + READ_PARAM_OFFSET,
188 value);
189
190 writel(value, devbus->base + READ_PARAM_OFFSET);
191
192 /* Set write timings */
193 value = w.sync_enable << SYNC_ENABLE_BIT |
194 w.wr_low << WR_LOW_BIT |
195 w.wr_high << WR_HIGH_BIT |
196 w.ale_wr;
197
198 dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n",
199 devbus->base + WRITE_PARAM_OFFSET,
200 value);
201
202 writel(value, devbus->base + WRITE_PARAM_OFFSET);
203
204 return 0;
205}
206
207static int mvebu_devbus_probe(struct platform_device *pdev)
208{
209 struct device *dev = &pdev->dev;
210 struct device_node *node = pdev->dev.of_node;
211 struct device_node *parent;
212 struct devbus *devbus;
213 struct resource *res;
214 struct clk *clk;
215 unsigned long rate;
216 const __be32 *ranges;
217 int err, cs;
218 int addr_cells, p_addr_cells, size_cells;
219 int ranges_len, tuple_len;
220 u32 base, size;
221
222 devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL);
223 if (!devbus)
224 return -ENOMEM;
225
226 devbus->dev = dev;
227 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
228 devbus->base = devm_ioremap_resource(&pdev->dev, res);
229 if (IS_ERR(devbus->base))
230 return PTR_ERR(devbus->base);
231
232 clk = devm_clk_get(&pdev->dev, NULL);
233 if (IS_ERR(clk))
234 return PTR_ERR(clk);
235 clk_prepare_enable(clk);
236
237 /*
238 * Obtain clock period in picoseconds,
239 * we need this in order to convert timing
240 * parameters from cycles to picoseconds.
241 */
242 rate = clk_get_rate(clk) / 1000;
243 devbus->tick_ps = 1000000000 / rate;
244
245 /* Read the device tree node and set the new timing parameters */
246 err = devbus_set_timing_params(devbus, node);
247 if (err < 0)
248 return err;
249
250 /*
251 * Allocate an address window for this device.
252 * If the device probing fails, then we won't be able to
253 * remove the allocated address decoding window.
254 *
255 * FIXME: This is only a temporary hack! We need to do this here
256 * because we still don't have device tree bindings for mbus.
257 * Once that support is added, we will declare these address windows
258 * statically in the device tree, and remove the window configuration
259 * from here.
260 */
261
262 /*
263 * Get the CS to choose the window string.
264 * This is a bit hacky, but it will be removed once the
265 * address windows are declared in the device tree.
266 */
267 cs = (((unsigned long)devbus->base) % 0x400) / 8;
268
269 /*
270 * Parse 'ranges' property to obtain a (base,size) window tuple.
271 * This will be removed once the address windows
272 * are declared in the device tree.
273 */
274 parent = of_get_parent(node);
275 if (!parent)
276 return -EINVAL;
277
278 p_addr_cells = of_n_addr_cells(parent);
279 of_node_put(parent);
280
281 addr_cells = of_n_addr_cells(node);
282 size_cells = of_n_size_cells(node);
283 tuple_len = (p_addr_cells + addr_cells + size_cells) * sizeof(__be32);
284
285 ranges = of_get_property(node, "ranges", &ranges_len);
286 if (ranges == NULL || ranges_len != tuple_len)
287 return -EINVAL;
288
289 base = of_translate_address(node, ranges + addr_cells);
290 if (base == OF_BAD_ADDR)
291 return -EINVAL;
292 size = of_read_number(ranges + addr_cells + p_addr_cells, size_cells);
293
294 /*
295 * Create an mbus address windows.
296 * FIXME: Remove this, together with the above code, once the
297 * address windows are declared in the device tree.
298 */
299 err = mvebu_mbus_add_window(devbus_wins[cs], base, size);
300 if (err < 0)
301 return err;
302
303 /*
304 * We need to create a child device explicitly from here to
305 * guarantee that the child will be probed after the timing
306 * parameters for the bus are written.
307 */
308 err = of_platform_populate(node, NULL, NULL, dev);
309 if (err < 0) {
310 mvebu_mbus_del_window(base, size);
311 return err;
312 }
313
314 return 0;
315}
316
317static const struct of_device_id mvebu_devbus_of_match[] = {
318 { .compatible = "marvell,mvebu-devbus" },
319 {},
320};
321MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match);
322
323static struct platform_driver mvebu_devbus_driver = {
324 .probe = mvebu_devbus_probe,
325 .driver = {
326 .name = "mvebu-devbus",
327 .owner = THIS_MODULE,
328 .of_match_table = mvebu_devbus_of_match,
329 },
330};
331
332static int __init mvebu_devbus_init(void)
333{
334 return platform_driver_register(&mvebu_devbus_driver);
335}
336module_init(mvebu_devbus_init);
337
338MODULE_LICENSE("GPL v2");
339MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
340MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller");