summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2013-03-21 12:59:14 -0400
committerJason Cooper <jason@lakedaemon.net>2013-03-28 15:04:16 -0400
commitfddddb52a6c4e2438f4514ed979183653ca0732a (patch)
tree4a4dc2d30762aa03ac70cb185f26b114503b104c /drivers
parent8bb9660418e05bb1845ac1a2428444d78e322cc7 (diff)
bus: introduce an Marvell EBU MBus driver
The Marvell EBU SoCs have a configurable physical address space layout: the physical ranges of memory used to address PCI(e) interfaces, NOR flashes, SRAM and various other types of memory are configurable by software, through a mechanism of so-called 'address decoding windows'. This new driver mvebu-mbus consolidates the existing code to address the configuration of these memory ranges, which is spread into mach-mvebu, mach-orion5x, mach-mv78xx0, mach-dove and mach-kirkwood. Following patches convert each Marvell EBU SoC family to use this driver, therefore removing the old code that was configuring the address decoding windows. It is worth mentioning that the MVEBU_MBUS Kconfig option is intentionally added as a blind option. The new driver implements and exports the mv_mbus_dram_info() function, which is used by various Marvell drivers throughout the tree to get access to window configuration parameters that they require. This function is also implemented in arch/arm/plat-orion/addr-map.c, which ultimately gets removed at the end of this patch series. So, in order to preserve bisectability, we want to ensure that *either* this new driver, *or* the legacy code in plat-orion/addr-map.c gets compiled in. By making MVEBU_MBUS a blind option, we are sure that only a platform that does 'select MVEBU_MBUS' will get this new driver compiled in. Therefore, throughout the next patches that convert the Marvell sub-architectures one after the other to this new driver, we add the 'select MVEBU_MBUS' and also ensure to remove plat-orion/addr-map.c from the build for this specific sub-architecture. This ensures that bisectability is preserved. Ealier versions of this driver had a DT binding, but since those were not yet agreed upon, they were removed. The driver still uses of_device_id to find the SoC specific details according to the string passed to mvebu_mbus_init(). The plan is to re-introduce a proper DT binding as a followup set of patches. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bus/Kconfig7
-rw-r--r--drivers/bus/Makefile1
-rw-r--r--drivers/bus/mvebu-mbus.c867
3 files changed, 875 insertions, 0 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 0f51ed687dc8..b05ecab915c4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -4,6 +4,13 @@
4 4
5menu "Bus devices" 5menu "Bus devices"
6 6
7config MVEBU_MBUS
8 bool
9 depends on PLAT_ORION
10 help
11 Driver needed for the MBus configuration on Marvell EBU SoCs
12 (Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP).
13
7config OMAP_OCP2SCP 14config OMAP_OCP2SCP
8 tristate "OMAP OCP2SCP DRIVER" 15 tristate "OMAP OCP2SCP DRIVER"
9 depends on ARCH_OMAP2PLUS 16 depends on ARCH_OMAP2PLUS
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 45d997c85453..3c7b53c12091 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -2,6 +2,7 @@
2# Makefile for the bus drivers. 2# Makefile for the bus drivers.
3# 3#
4 4
5obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
5obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o 6obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
6 7
7# Interconnect bus driver for OMAP SoCs. 8# Interconnect bus driver for OMAP SoCs.
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
new file mode 100644
index 000000000000..586d03e29e9e
--- /dev/null
+++ b/drivers/bus/mvebu-mbus.c
@@ -0,0 +1,867 @@
1/*
2 * Address map functions for Marvell EBU SoCs (Kirkwood, Armada
3 * 370/XP, Dove, Orion5x and MV78xx0)
4 *
5 * This file is licensed under the terms of the GNU General Public
6 * License version 2. This program is licensed "as is" without any
7 * warranty of any kind, whether express or implied.
8 *
9 * The Marvell EBU SoCs have a configurable physical address space:
10 * the physical address at which certain devices (PCIe, NOR, NAND,
11 * etc.) sit can be configured. The configuration takes place through
12 * two sets of registers:
13 *
14 * - One to configure the access of the CPU to the devices. Depending
15 * on the families, there are between 8 and 20 configurable windows,
16 * each can be use to create a physical memory window that maps to a
17 * specific device. Devices are identified by a tuple (target,
18 * attribute).
19 *
20 * - One to configure the access to the CPU to the SDRAM. There are
21 * either 2 (for Dove) or 4 (for other families) windows to map the
22 * SDRAM into the physical address space.
23 *
24 * This driver:
25 *
26 * - Reads out the SDRAM address decoding windows at initialization
27 * time, and fills the mvebu_mbus_dram_info structure with these
28 * informations. The exported function mv_mbus_dram_info() allow
29 * device drivers to get those informations related to the SDRAM
30 * address decoding windows. This is because devices also have their
31 * own windows (configured through registers that are part of each
32 * device register space), and therefore the drivers for Marvell
33 * devices have to configure those device -> SDRAM windows to ensure
34 * that DMA works properly.
35 *
36 * - Provides an API for platform code or device drivers to
37 * dynamically add or remove address decoding windows for the CPU ->
38 * device accesses. This API is mvebu_mbus_add_window(),
39 * mvebu_mbus_add_window_remap_flags() and
40 * mvebu_mbus_del_window(). Since the (target, attribute) values
41 * differ from one SoC family to another, the API uses a 'const char
42 * *' string to identify devices, and this driver is responsible for
43 * knowing the mapping between the name of a device and its
44 * corresponding (target, attribute) in the current SoC family.
45 *
46 * - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to
47 * see the list of CPU -> SDRAM windows and their configuration
48 * (file 'sdram') and the list of CPU -> devices windows and their
49 * configuration (file 'devices').
50 */
51
52#include <linux/kernel.h>
53#include <linux/module.h>
54#include <linux/init.h>
55#include <linux/mbus.h>
56#include <linux/io.h>
57#include <linux/ioport.h>
58#include <linux/of.h>
59#include <linux/of_address.h>
60#include <linux/debugfs.h>
61
62/*
63 * DDR target is the same on all platforms.
64 */
65#define TARGET_DDR 0
66
67/*
68 * CPU Address Decode Windows registers
69 */
70#define WIN_CTRL_OFF 0x0000
71#define WIN_CTRL_ENABLE BIT(0)
72#define WIN_CTRL_TGT_MASK 0xf0
73#define WIN_CTRL_TGT_SHIFT 4
74#define WIN_CTRL_ATTR_MASK 0xff00
75#define WIN_CTRL_ATTR_SHIFT 8
76#define WIN_CTRL_SIZE_MASK 0xffff0000
77#define WIN_CTRL_SIZE_SHIFT 16
78#define WIN_BASE_OFF 0x0004
79#define WIN_BASE_LOW 0xffff0000
80#define WIN_BASE_HIGH 0xf
81#define WIN_REMAP_LO_OFF 0x0008
82#define WIN_REMAP_LOW 0xffff0000
83#define WIN_REMAP_HI_OFF 0x000c
84
85#define ATTR_HW_COHERENCY (0x1 << 4)
86
87#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3))
88#define DDR_BASE_CS_HIGH_MASK 0xf
89#define DDR_BASE_CS_LOW_MASK 0xff000000
90#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3))
91#define DDR_SIZE_ENABLED BIT(0)
92#define DDR_SIZE_CS_MASK 0x1c
93#define DDR_SIZE_CS_SHIFT 2
94#define DDR_SIZE_MASK 0xff000000
95
96#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
97
98struct mvebu_mbus_mapping {
99 const char *name;
100 u8 target;
101 u8 attr;
102 u8 attrmask;
103};
104
105/*
106 * Masks used for the 'attrmask' field of mvebu_mbus_mapping. They
107 * allow to get the real attribute value, discarding the special bits
108 * used to select a PCI MEM region or a PCI WA region. This allows the
109 * debugfs code to reverse-match the name of a device from its
110 * target/attr values.
111 *
112 * For all devices except PCI, all bits of 'attr' must be
113 * considered. For most SoCs, only bit 3 should be ignored (it allows
114 * to select between PCI MEM and PCI I/O). On Orion5x however, there
115 * is the special bit 5 to select a PCI WA region.
116 */
117#define MAPDEF_NOMASK 0xff
118#define MAPDEF_PCIMASK 0xf7
119#define MAPDEF_ORIONPCIMASK 0xd7
120
121/* Macro used to define one mvebu_mbus_mapping entry */
122#define MAPDEF(__n, __t, __a, __m) \
123 { .name = __n, .target = __t, .attr = __a, .attrmask = __m }
124
125struct mvebu_mbus_state;
126
127struct mvebu_mbus_soc_data {
128 unsigned int num_wins;
129 unsigned int num_remappable_wins;
130 unsigned int (*win_cfg_offset)(const int win);
131 void (*setup_cpu_target)(struct mvebu_mbus_state *s);
132 int (*show_cpu_target)(struct mvebu_mbus_state *s,
133 struct seq_file *seq, void *v);
134 const struct mvebu_mbus_mapping *map;
135};
136
137struct mvebu_mbus_state {
138 void __iomem *mbuswins_base;
139 void __iomem *sdramwins_base;
140 struct dentry *debugfs_root;
141 struct dentry *debugfs_sdram;
142 struct dentry *debugfs_devs;
143 const struct mvebu_mbus_soc_data *soc;
144 int hw_io_coherency;
145};
146
147static struct mvebu_mbus_state mbus_state;
148
149static struct mbus_dram_target_info mvebu_mbus_dram_info;
150const struct mbus_dram_target_info *mv_mbus_dram_info(void)
151{
152 return &mvebu_mbus_dram_info;
153}
154EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
155
156/*
157 * Functions to manipulate the address decoding windows
158 */
159
160static void mvebu_mbus_read_window(struct mvebu_mbus_state *mbus,
161 int win, int *enabled, u64 *base,
162 u32 *size, u8 *target, u8 *attr,
163 u64 *remap)
164{
165 void __iomem *addr = mbus->mbuswins_base +
166 mbus->soc->win_cfg_offset(win);
167 u32 basereg = readl(addr + WIN_BASE_OFF);
168 u32 ctrlreg = readl(addr + WIN_CTRL_OFF);
169
170 if (!(ctrlreg & WIN_CTRL_ENABLE)) {
171 *enabled = 0;
172 return;
173 }
174
175 *enabled = 1;
176 *base = ((u64)basereg & WIN_BASE_HIGH) << 32;
177 *base |= (basereg & WIN_BASE_LOW);
178 *size = (ctrlreg | ~WIN_CTRL_SIZE_MASK) + 1;
179
180 if (target)
181 *target = (ctrlreg & WIN_CTRL_TGT_MASK) >> WIN_CTRL_TGT_SHIFT;
182
183 if (attr)
184 *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT;
185
186 if (remap) {
187 if (win < mbus->soc->num_remappable_wins) {
188 u32 remap_low = readl(addr + WIN_REMAP_LO_OFF);
189 u32 remap_hi = readl(addr + WIN_REMAP_HI_OFF);
190 *remap = ((u64)remap_hi << 32) | remap_low;
191 } else
192 *remap = 0;
193 }
194}
195
196static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus,
197 int win)
198{
199 void __iomem *addr;
200
201 addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win);
202
203 writel(0, addr + WIN_BASE_OFF);
204 writel(0, addr + WIN_CTRL_OFF);
205 if (win < mbus->soc->num_remappable_wins) {
206 writel(0, addr + WIN_REMAP_LO_OFF);
207 writel(0, addr + WIN_REMAP_HI_OFF);
208 }
209}
210
211/* Checks whether the given window number is available */
212static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus,
213 const int win)
214{
215 void __iomem *addr = mbus->mbuswins_base +
216 mbus->soc->win_cfg_offset(win);
217 u32 ctrl = readl(addr + WIN_CTRL_OFF);
218 return !(ctrl & WIN_CTRL_ENABLE);
219}
220
221/*
222 * Checks whether the given (base, base+size) area doesn't overlap an
223 * existing region
224 */
225static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus,
226 phys_addr_t base, size_t size,
227 u8 target, u8 attr)
228{
229 u64 end = (u64)base + size;
230 int win;
231
232 for (win = 0; win < mbus->soc->num_wins; win++) {
233 u64 wbase, wend;
234 u32 wsize;
235 u8 wtarget, wattr;
236 int enabled;
237
238 mvebu_mbus_read_window(mbus, win,
239 &enabled, &wbase, &wsize,
240 &wtarget, &wattr, NULL);
241
242 if (!enabled)
243 continue;
244
245 wend = wbase + wsize;
246
247 /*
248 * Check if the current window overlaps with the
249 * proposed physical range
250 */
251 if ((u64)base < wend && end > wbase)
252 return 0;
253
254 /*
255 * Check if target/attribute conflicts
256 */
257 if (target == wtarget && attr == wattr)
258 return 0;
259 }
260
261 return 1;
262}
263
264static int mvebu_mbus_find_window(struct mvebu_mbus_state *mbus,
265 phys_addr_t base, size_t size)
266{
267 int win;
268
269 for (win = 0; win < mbus->soc->num_wins; win++) {
270 u64 wbase;
271 u32 wsize;
272 int enabled;
273
274 mvebu_mbus_read_window(mbus, win,
275 &enabled, &wbase, &wsize,
276 NULL, NULL, NULL);
277
278 if (!enabled)
279 continue;
280
281 if (base == wbase && size == wsize)
282 return win;
283 }
284
285 return -ENODEV;
286}
287
288static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
289 int win, phys_addr_t base, size_t size,
290 phys_addr_t remap, u8 target,
291 u8 attr)
292{
293 void __iomem *addr = mbus->mbuswins_base +
294 mbus->soc->win_cfg_offset(win);
295 u32 ctrl, remap_addr;
296
297 ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
298 (attr << WIN_CTRL_ATTR_SHIFT) |
299 (target << WIN_CTRL_TGT_SHIFT) |
300 WIN_CTRL_ENABLE;
301
302 writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
303 writel(ctrl, addr + WIN_CTRL_OFF);
304 if (win < mbus->soc->num_remappable_wins) {
305 if (remap == MVEBU_MBUS_NO_REMAP)
306 remap_addr = base;
307 else
308 remap_addr = remap;
309 writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF);
310 writel(0, addr + WIN_REMAP_HI_OFF);
311 }
312
313 return 0;
314}
315
316static int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus,
317 phys_addr_t base, size_t size,
318 phys_addr_t remap, u8 target,
319 u8 attr)
320{
321 int win;
322
323 if (remap == MVEBU_MBUS_NO_REMAP) {
324 for (win = mbus->soc->num_remappable_wins;
325 win < mbus->soc->num_wins; win++)
326 if (mvebu_mbus_window_is_free(mbus, win))
327 return mvebu_mbus_setup_window(mbus, win, base,
328 size, remap,
329 target, attr);
330 }
331
332
333 for (win = 0; win < mbus->soc->num_wins; win++)
334 if (mvebu_mbus_window_is_free(mbus, win))
335 return mvebu_mbus_setup_window(mbus, win, base, size,
336 remap, target, attr);
337
338 return -ENOMEM;
339}
340
341/*
342 * Debugfs debugging
343 */
344
345/* Common function used for Dove, Kirkwood, Armada 370/XP and Orion 5x */
346static int mvebu_sdram_debug_show_orion(struct mvebu_mbus_state *mbus,
347 struct seq_file *seq, void *v)
348{
349 int i;
350
351 for (i = 0; i < 4; i++) {
352 u32 basereg = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
353 u32 sizereg = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
354 u64 base;
355 u32 size;
356
357 if (!(sizereg & DDR_SIZE_ENABLED)) {
358 seq_printf(seq, "[%d] disabled\n", i);
359 continue;
360 }
361
362 base = ((u64)basereg & DDR_BASE_CS_HIGH_MASK) << 32;
363 base |= basereg & DDR_BASE_CS_LOW_MASK;
364 size = (sizereg | ~DDR_SIZE_MASK);
365
366 seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n",
367 i, (unsigned long long)base,
368 (unsigned long long)base + size + 1,
369 (sizereg & DDR_SIZE_CS_MASK) >> DDR_SIZE_CS_SHIFT);
370 }
371
372 return 0;
373}
374
375/* Special function for Dove */
376static int mvebu_sdram_debug_show_dove(struct mvebu_mbus_state *mbus,
377 struct seq_file *seq, void *v)
378{
379 int i;
380
381 for (i = 0; i < 2; i++) {
382 u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
383 u64 base;
384 u32 size;
385
386 if (!(map & 1)) {
387 seq_printf(seq, "[%d] disabled\n", i);
388 continue;
389 }
390
391 base = map & 0xff800000;
392 size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
393
394 seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n",
395 i, (unsigned long long)base,
396 (unsigned long long)base + size, i);
397 }
398
399 return 0;
400}
401
402static int mvebu_sdram_debug_show(struct seq_file *seq, void *v)
403{
404 struct mvebu_mbus_state *mbus = &mbus_state;
405 return mbus->soc->show_cpu_target(mbus, seq, v);
406}
407
408static int mvebu_sdram_debug_open(struct inode *inode, struct file *file)
409{
410 return single_open(file, mvebu_sdram_debug_show, inode->i_private);
411}
412
413static const struct file_operations mvebu_sdram_debug_fops = {
414 .open = mvebu_sdram_debug_open,
415 .read = seq_read,
416 .llseek = seq_lseek,
417 .release = single_release,
418};
419
420static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
421{
422 struct mvebu_mbus_state *mbus = &mbus_state;
423 int win;
424
425 for (win = 0; win < mbus->soc->num_wins; win++) {
426 u64 wbase, wremap;
427 u32 wsize;
428 u8 wtarget, wattr;
429 int enabled, i;
430 const char *name;
431
432 mvebu_mbus_read_window(mbus, win,
433 &enabled, &wbase, &wsize,
434 &wtarget, &wattr, &wremap);
435
436 if (!enabled) {
437 seq_printf(seq, "[%02d] disabled\n", win);
438 continue;
439 }
440
441
442 for (i = 0; mbus->soc->map[i].name; i++)
443 if (mbus->soc->map[i].target == wtarget &&
444 mbus->soc->map[i].attr ==
445 (wattr & mbus->soc->map[i].attrmask))
446 break;
447
448 name = mbus->soc->map[i].name ?: "unknown";
449
450 seq_printf(seq, "[%02d] %016llx - %016llx : %s",
451 win, (unsigned long long)wbase,
452 (unsigned long long)(wbase + wsize), name);
453
454 if (win < mbus->soc->num_remappable_wins) {
455 seq_printf(seq, " (remap %016llx)\n",
456 (unsigned long long)wremap);
457 } else
458 seq_printf(seq, "\n");
459 }
460
461 return 0;
462}
463
464static int mvebu_devs_debug_open(struct inode *inode, struct file *file)
465{
466 return single_open(file, mvebu_devs_debug_show, inode->i_private);
467}
468
469static const struct file_operations mvebu_devs_debug_fops = {
470 .open = mvebu_devs_debug_open,
471 .read = seq_read,
472 .llseek = seq_lseek,
473 .release = single_release,
474};
475
476/*
477 * SoC-specific functions and definitions
478 */
479
480static unsigned int orion_mbus_win_offset(int win)
481{
482 return win << 4;
483}
484
485static unsigned int armada_370_xp_mbus_win_offset(int win)
486{
487 /* The register layout is a bit annoying and the below code
488 * tries to cope with it.
489 * - At offset 0x0, there are the registers for the first 8
490 * windows, with 4 registers of 32 bits per window (ctrl,
491 * base, remap low, remap high)
492 * - Then at offset 0x80, there is a hole of 0x10 bytes for
493 * the internal registers base address and internal units
494 * sync barrier register.
495 * - Then at offset 0x90, there the registers for 12
496 * windows, with only 2 registers of 32 bits per window
497 * (ctrl, base).
498 */
499 if (win < 8)
500 return win << 4;
501 else
502 return 0x90 + ((win - 8) << 3);
503}
504
505static unsigned int mv78xx0_mbus_win_offset(int win)
506{
507 if (win < 8)
508 return win << 4;
509 else
510 return 0x900 + ((win - 8) << 4);
511}
512
513static void __init
514mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
515{
516 int i;
517 int cs;
518
519 mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
520
521 for (i = 0, cs = 0; i < 4; i++) {
522 u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
523 u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
524
525 /*
526 * We only take care of entries for which the chip
527 * select is enabled, and that don't have high base
528 * address bits set (devices can only access the first
529 * 32 bits of the memory).
530 */
531 if ((size & DDR_SIZE_ENABLED) &&
532 !(base & DDR_BASE_CS_HIGH_MASK)) {
533 struct mbus_dram_window *w;
534
535 w = &mvebu_mbus_dram_info.cs[cs++];
536 w->cs_index = i;
537 w->mbus_attr = 0xf & ~(1 << i);
538 if (mbus->hw_io_coherency)
539 w->mbus_attr |= ATTR_HW_COHERENCY;
540 w->base = base & DDR_BASE_CS_LOW_MASK;
541 w->size = (size | ~DDR_SIZE_MASK) + 1;
542 }
543 }
544 mvebu_mbus_dram_info.num_cs = cs;
545}
546
547static void __init
548mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
549{
550 int i;
551 int cs;
552
553 mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
554
555 for (i = 0, cs = 0; i < 2; i++) {
556 u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
557
558 /*
559 * Chip select enabled?
560 */
561 if (map & 1) {
562 struct mbus_dram_window *w;
563
564 w = &mvebu_mbus_dram_info.cs[cs++];
565 w->cs_index = i;
566 w->mbus_attr = 0; /* CS address decoding done inside */
567 /* the DDR controller, no need to */
568 /* provide attributes */
569 w->base = map & 0xff800000;
570 w->size = 0x100000 << (((map & 0x000f0000) >> 16) - 4);
571 }
572 }
573
574 mvebu_mbus_dram_info.num_cs = cs;
575}
576
577static const struct mvebu_mbus_mapping armada_370_map[] = {
578 MAPDEF("bootrom", 1, 0xe0, MAPDEF_NOMASK),
579 MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
580 MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
581 MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
582 MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
583 MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
584 MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
585 MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
586 {},
587};
588
589static const struct mvebu_mbus_soc_data armada_370_mbus_data = {
590 .num_wins = 20,
591 .num_remappable_wins = 8,
592 .win_cfg_offset = armada_370_xp_mbus_win_offset,
593 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
594 .show_cpu_target = mvebu_sdram_debug_show_orion,
595 .map = armada_370_map,
596};
597
598static const struct mvebu_mbus_mapping armada_xp_map[] = {
599 MAPDEF("bootrom", 1, 0x1d, MAPDEF_NOMASK),
600 MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
601 MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
602 MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
603 MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
604 MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
605 MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
606 MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
607 MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
608 MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
609 MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
610 MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
611 MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
612 MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
613 MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
614 MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
615 {},
616};
617
618static const struct mvebu_mbus_soc_data armada_xp_mbus_data = {
619 .num_wins = 20,
620 .num_remappable_wins = 8,
621 .win_cfg_offset = armada_370_xp_mbus_win_offset,
622 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
623 .show_cpu_target = mvebu_sdram_debug_show_orion,
624 .map = armada_xp_map,
625};
626
627static const struct mvebu_mbus_mapping kirkwood_map[] = {
628 MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
629 MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
630 MAPDEF("sram", 3, 0x01, MAPDEF_NOMASK),
631 MAPDEF("nand", 1, 0x2f, MAPDEF_NOMASK),
632 {},
633};
634
635static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
636 .num_wins = 8,
637 .num_remappable_wins = 4,
638 .win_cfg_offset = orion_mbus_win_offset,
639 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
640 .show_cpu_target = mvebu_sdram_debug_show_orion,
641 .map = kirkwood_map,
642};
643
644static const struct mvebu_mbus_mapping dove_map[] = {
645 MAPDEF("pcie0.0", 0x4, 0xe0, MAPDEF_PCIMASK),
646 MAPDEF("pcie1.0", 0x8, 0xe0, MAPDEF_PCIMASK),
647 MAPDEF("cesa", 0x3, 0x01, MAPDEF_NOMASK),
648 MAPDEF("bootrom", 0x1, 0xfd, MAPDEF_NOMASK),
649 MAPDEF("scratchpad", 0xd, 0x0, MAPDEF_NOMASK),
650 {},
651};
652
653static const struct mvebu_mbus_soc_data dove_mbus_data = {
654 .num_wins = 8,
655 .num_remappable_wins = 4,
656 .win_cfg_offset = orion_mbus_win_offset,
657 .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
658 .show_cpu_target = mvebu_sdram_debug_show_dove,
659 .map = dove_map,
660};
661
662static const struct mvebu_mbus_mapping orion5x_map[] = {
663 MAPDEF("pcie0.0", 4, 0x51, MAPDEF_ORIONPCIMASK),
664 MAPDEF("pci0.0", 3, 0x51, MAPDEF_ORIONPCIMASK),
665 MAPDEF("devbus-boot", 1, 0x0f, MAPDEF_NOMASK),
666 MAPDEF("devbus-cs0", 1, 0x1e, MAPDEF_NOMASK),
667 MAPDEF("devbus-cs1", 1, 0x1d, MAPDEF_NOMASK),
668 MAPDEF("devbus-cs2", 1, 0x1b, MAPDEF_NOMASK),
669 MAPDEF("sram", 0, 0x00, MAPDEF_NOMASK),
670 {},
671};
672
673/*
674 * Some variants of Orion5x have 4 remappable windows, some other have
675 * only two of them.
676 */
677static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
678 .num_wins = 8,
679 .num_remappable_wins = 4,
680 .win_cfg_offset = orion_mbus_win_offset,
681 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
682 .show_cpu_target = mvebu_sdram_debug_show_orion,
683 .map = orion5x_map,
684};
685
686static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
687 .num_wins = 8,
688 .num_remappable_wins = 2,
689 .win_cfg_offset = orion_mbus_win_offset,
690 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
691 .show_cpu_target = mvebu_sdram_debug_show_orion,
692 .map = orion5x_map,
693};
694
695static const struct mvebu_mbus_mapping mv78xx0_map[] = {
696 MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
697 MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
698 MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
699 MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
700 MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
701 MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
702 MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
703 MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
704 MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
705 MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
706 {},
707};
708
709static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
710 .num_wins = 14,
711 .num_remappable_wins = 8,
712 .win_cfg_offset = mv78xx0_mbus_win_offset,
713 .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
714 .show_cpu_target = mvebu_sdram_debug_show_orion,
715 .map = mv78xx0_map,
716};
717
718/*
719 * The driver doesn't yet have a DT binding because the details of
720 * this DT binding still need to be sorted out. However, as a
721 * preparation, we already use of_device_id to match a SoC description
722 * string against the SoC specific details of this driver.
723 */
724static const struct of_device_id of_mvebu_mbus_ids[] = {
725 { .compatible = "marvell,armada370-mbus",
726 .data = &armada_370_mbus_data, },
727 { .compatible = "marvell,armadaxp-mbus",
728 .data = &armada_xp_mbus_data, },
729 { .compatible = "marvell,kirkwood-mbus",
730 .data = &kirkwood_mbus_data, },
731 { .compatible = "marvell,dove-mbus",
732 .data = &dove_mbus_data, },
733 { .compatible = "marvell,orion5x-88f5281-mbus",
734 .data = &orion5x_4win_mbus_data, },
735 { .compatible = "marvell,orion5x-88f5182-mbus",
736 .data = &orion5x_2win_mbus_data, },
737 { .compatible = "marvell,orion5x-88f5181-mbus",
738 .data = &orion5x_2win_mbus_data, },
739 { .compatible = "marvell,orion5x-88f6183-mbus",
740 .data = &orion5x_4win_mbus_data, },
741 { .compatible = "marvell,mv78xx0-mbus",
742 .data = &mv78xx0_mbus_data, },
743 { },
744};
745
746/*
747 * Public API of the driver
748 */
749int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base,
750 size_t size, phys_addr_t remap,
751 unsigned int flags)
752{
753 struct mvebu_mbus_state *s = &mbus_state;
754 u8 target, attr;
755 int i;
756
757 if (!s->soc->map)
758 return -ENODEV;
759
760 for (i = 0; s->soc->map[i].name; i++)
761 if (!strcmp(s->soc->map[i].name, devname))
762 break;
763
764 if (!s->soc->map[i].name) {
765 pr_err("mvebu-mbus: unknown device '%s'\n", devname);
766 return -ENODEV;
767 }
768
769 target = s->soc->map[i].target;
770 attr = s->soc->map[i].attr;
771
772 if (flags == MVEBU_MBUS_PCI_MEM)
773 attr |= 0x8;
774 else if (flags == MVEBU_MBUS_PCI_WA)
775 attr |= 0x28;
776
777 if (!mvebu_mbus_window_conflicts(s, base, size, target, attr)) {
778 pr_err("mvebu-mbus: cannot add window '%s', conflicts with another window\n",
779 devname);
780 return -EINVAL;
781 }
782
783 return mvebu_mbus_alloc_window(s, base, size, remap, target, attr);
784
785}
786
787int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size)
788{
789 return mvebu_mbus_add_window_remap_flags(devname, base, size,
790 MVEBU_MBUS_NO_REMAP, 0);
791}
792
793int mvebu_mbus_del_window(phys_addr_t base, size_t size)
794{
795 int win;
796
797 win = mvebu_mbus_find_window(&mbus_state, base, size);
798 if (win < 0)
799 return win;
800
801 mvebu_mbus_disable_window(&mbus_state, win);
802 return 0;
803}
804
805static __init int mvebu_mbus_debugfs_init(void)
806{
807 struct mvebu_mbus_state *s = &mbus_state;
808
809 /*
810 * If no base has been initialized, doesn't make sense to
811 * register the debugfs entries. We may be on a multiplatform
812 * kernel that isn't running a Marvell EBU SoC.
813 */
814 if (!s->mbuswins_base)
815 return 0;
816
817 s->debugfs_root = debugfs_create_dir("mvebu-mbus", NULL);
818 if (s->debugfs_root) {
819 s->debugfs_sdram = debugfs_create_file("sdram", S_IRUGO,
820 s->debugfs_root, NULL,
821 &mvebu_sdram_debug_fops);
822 s->debugfs_devs = debugfs_create_file("devices", S_IRUGO,
823 s->debugfs_root, NULL,
824 &mvebu_devs_debug_fops);
825 }
826
827 return 0;
828}
829fs_initcall(mvebu_mbus_debugfs_init);
830
831int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
832 size_t mbuswins_size,
833 phys_addr_t sdramwins_phys_base,
834 size_t sdramwins_size)
835{
836 struct mvebu_mbus_state *mbus = &mbus_state;
837 const struct of_device_id *of_id;
838 int win;
839
840 for (of_id = of_mvebu_mbus_ids; of_id->compatible; of_id++)
841 if (!strcmp(of_id->compatible, soc))
842 break;
843
844 if (!of_id->compatible) {
845 pr_err("mvebu-mbus: could not find a matching SoC family\n");
846 return -ENODEV;
847 }
848
849 mbus->soc = of_id->data;
850
851 mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
852 if (!mbus->mbuswins_base)
853 return -ENOMEM;
854
855 mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size);
856 if (!mbus->sdramwins_base) {
857 iounmap(mbus_state.mbuswins_base);
858 return -ENOMEM;
859 }
860
861 for (win = 0; win < mbus->soc->num_wins; win++)
862 mvebu_mbus_disable_window(mbus, win);
863
864 mbus->soc->setup_cpu_target(mbus);
865
866 return 0;
867}