Commit | Line | Data |
---|---|---|
009f1315 GC |
1 | /* |
2 | * Coherency fabric (Aurora) support for Armada 370 and XP platforms. | |
3 | * | |
4 | * Copyright (C) 2012 Marvell | |
5 | * | |
6 | * Yehuda Yitschak <yehuday@marvell.com> | |
7 | * Gregory Clement <gregory.clement@free-electrons.com> | |
8 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | |
9 | * | |
10 | * This file is licensed under the terms of the GNU General Public | |
11 | * License version 2. This program is licensed "as is" without any | |
12 | * warranty of any kind, whether express or implied. | |
13 | * | |
14 | * The Armada 370 and Armada XP SOCs have a coherency fabric which is | |
15 | * responsible for ensuring hardware coherency between all CPUs and between | |
16 | * CPUs and I/O masters. This file initializes the coherency fabric and | |
17 | * supplies basic routines for configuring and controlling hardware coherency | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/of_address.h> | |
23 | #include <linux/io.h> | |
24 | #include <linux/smp.h> | |
e60304f8 GC |
25 | #include <linux/dma-mapping.h> |
26 | #include <linux/platform_device.h> | |
009f1315 GC |
27 | #include <asm/smp_plat.h> |
28 | #include "armada-370-xp.h" | |
29 | ||
30 | /* | |
31 | * Some functions in this file are called very early during SMP | |
32 | * initialization. At that time the device tree framework is not yet | |
33 | * ready, and it is not possible to get the register address to | |
34 | * ioremap it. That's why the pointer below is given with an initial | |
35 | * value matching its virtual mapping | |
36 | */ | |
37 | static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200; | |
e60304f8 | 38 | static void __iomem *coherency_cpu_base; |
009f1315 GC |
39 | |
40 | /* Coherency fabric registers */ | |
41 | #define COHERENCY_FABRIC_CFG_OFFSET 0x4 | |
42 | ||
e60304f8 GC |
43 | #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 |
44 | ||
009f1315 GC |
45 | static struct of_device_id of_coherency_table[] = { |
46 | {.compatible = "marvell,coherency-fabric"}, | |
47 | { /* end of list */ }, | |
48 | }; | |
49 | ||
009f1315 GC |
50 | /* Function defined in coherency_ll.S */ |
51 | int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); | |
52 | ||
53 | int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) | |
54 | { | |
55 | if (!coherency_base) { | |
56 | pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); | |
57 | pr_warn("Coherency fabric is not initialized\n"); | |
58 | return 1; | |
59 | } | |
60 | ||
61 | return ll_set_cpu_coherent(coherency_base, hw_cpu_id); | |
62 | } | |
63 | ||
e60304f8 GC |
64 | static inline void mvebu_hwcc_sync_io_barrier(void) |
65 | { | |
66 | writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); | |
67 | while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); | |
68 | } | |
69 | ||
70 | static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page, | |
71 | unsigned long offset, size_t size, | |
72 | enum dma_data_direction dir, | |
73 | struct dma_attrs *attrs) | |
74 | { | |
75 | if (dir != DMA_TO_DEVICE) | |
76 | mvebu_hwcc_sync_io_barrier(); | |
77 | return pfn_to_dma(dev, page_to_pfn(page)) + offset; | |
78 | } | |
79 | ||
80 | ||
81 | static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, | |
82 | size_t size, enum dma_data_direction dir, | |
83 | struct dma_attrs *attrs) | |
84 | { | |
85 | if (dir != DMA_TO_DEVICE) | |
86 | mvebu_hwcc_sync_io_barrier(); | |
87 | } | |
88 | ||
89 | static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle, | |
90 | size_t size, enum dma_data_direction dir) | |
91 | { | |
92 | if (dir != DMA_TO_DEVICE) | |
93 | mvebu_hwcc_sync_io_barrier(); | |
94 | } | |
95 | ||
96 | static struct dma_map_ops mvebu_hwcc_dma_ops = { | |
97 | .alloc = arm_dma_alloc, | |
98 | .free = arm_dma_free, | |
99 | .mmap = arm_dma_mmap, | |
100 | .map_page = mvebu_hwcc_dma_map_page, | |
101 | .unmap_page = mvebu_hwcc_dma_unmap_page, | |
102 | .get_sgtable = arm_dma_get_sgtable, | |
103 | .map_sg = arm_dma_map_sg, | |
104 | .unmap_sg = arm_dma_unmap_sg, | |
105 | .sync_single_for_cpu = mvebu_hwcc_dma_sync, | |
106 | .sync_single_for_device = mvebu_hwcc_dma_sync, | |
107 | .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, | |
108 | .sync_sg_for_device = arm_dma_sync_sg_for_device, | |
109 | .set_dma_mask = arm_dma_set_mask, | |
110 | }; | |
111 | ||
112 | static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, | |
113 | unsigned long event, void *__dev) | |
114 | { | |
115 | struct device *dev = __dev; | |
116 | ||
117 | if (event != BUS_NOTIFY_ADD_DEVICE) | |
118 | return NOTIFY_DONE; | |
119 | set_dma_ops(dev, &mvebu_hwcc_dma_ops); | |
120 | ||
121 | return NOTIFY_OK; | |
122 | } | |
123 | ||
124 | static struct notifier_block mvebu_hwcc_platform_nb = { | |
125 | .notifier_call = mvebu_hwcc_platform_notifier, | |
126 | }; | |
127 | ||
009f1315 GC |
128 | int __init coherency_init(void) |
129 | { | |
130 | struct device_node *np; | |
131 | ||
132 | np = of_find_matching_node(NULL, of_coherency_table); | |
133 | if (np) { | |
134 | pr_info("Initializing Coherency fabric\n"); | |
135 | coherency_base = of_iomap(np, 0); | |
e60304f8 GC |
136 | coherency_cpu_base = of_iomap(np, 1); |
137 | set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); | |
138 | bus_register_notifier(&platform_bus_type, | |
139 | &mvebu_hwcc_platform_nb); | |
009f1315 GC |
140 | } |
141 | ||
142 | return 0; | |
143 | } |