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 | 27 | #include <asm/smp_plat.h> |
580ff0ee | 28 | #include <asm/cacheflush.h> |
009f1315 GC |
29 | #include "armada-370-xp.h" |
30 | ||
8bd26e3a | 31 | unsigned long coherency_phys_base; |
865e0527 | 32 | static void __iomem *coherency_base; |
e60304f8 | 33 | static void __iomem *coherency_cpu_base; |
009f1315 GC |
34 | |
35 | /* Coherency fabric registers */ | |
36 | #define COHERENCY_FABRIC_CFG_OFFSET 0x4 | |
37 | ||
e60304f8 GC |
38 | #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 |
39 | ||
009f1315 GC |
40 | static struct of_device_id of_coherency_table[] = { |
41 | {.compatible = "marvell,coherency-fabric"}, | |
42 | { /* end of list */ }, | |
43 | }; | |
44 | ||
009f1315 GC |
45 | /* Function defined in coherency_ll.S */ |
46 | int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); | |
47 | ||
48 | int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) | |
49 | { | |
50 | if (!coherency_base) { | |
51 | pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); | |
52 | pr_warn("Coherency fabric is not initialized\n"); | |
53 | return 1; | |
54 | } | |
55 | ||
56 | return ll_set_cpu_coherent(coherency_base, hw_cpu_id); | |
57 | } | |
58 | ||
e60304f8 GC |
59 | static inline void mvebu_hwcc_sync_io_barrier(void) |
60 | { | |
61 | writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); | |
62 | while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); | |
63 | } | |
64 | ||
65 | static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page, | |
66 | unsigned long offset, size_t size, | |
67 | enum dma_data_direction dir, | |
68 | struct dma_attrs *attrs) | |
69 | { | |
70 | if (dir != DMA_TO_DEVICE) | |
71 | mvebu_hwcc_sync_io_barrier(); | |
72 | return pfn_to_dma(dev, page_to_pfn(page)) + offset; | |
73 | } | |
74 | ||
75 | ||
76 | static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, | |
77 | size_t size, enum dma_data_direction dir, | |
78 | struct dma_attrs *attrs) | |
79 | { | |
80 | if (dir != DMA_TO_DEVICE) | |
81 | mvebu_hwcc_sync_io_barrier(); | |
82 | } | |
83 | ||
84 | static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle, | |
85 | size_t size, enum dma_data_direction dir) | |
86 | { | |
87 | if (dir != DMA_TO_DEVICE) | |
88 | mvebu_hwcc_sync_io_barrier(); | |
89 | } | |
90 | ||
91 | static struct dma_map_ops mvebu_hwcc_dma_ops = { | |
92 | .alloc = arm_dma_alloc, | |
93 | .free = arm_dma_free, | |
94 | .mmap = arm_dma_mmap, | |
95 | .map_page = mvebu_hwcc_dma_map_page, | |
96 | .unmap_page = mvebu_hwcc_dma_unmap_page, | |
97 | .get_sgtable = arm_dma_get_sgtable, | |
98 | .map_sg = arm_dma_map_sg, | |
99 | .unmap_sg = arm_dma_unmap_sg, | |
100 | .sync_single_for_cpu = mvebu_hwcc_dma_sync, | |
101 | .sync_single_for_device = mvebu_hwcc_dma_sync, | |
102 | .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, | |
103 | .sync_sg_for_device = arm_dma_sync_sg_for_device, | |
104 | .set_dma_mask = arm_dma_set_mask, | |
105 | }; | |
106 | ||
107 | static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, | |
108 | unsigned long event, void *__dev) | |
109 | { | |
110 | struct device *dev = __dev; | |
111 | ||
112 | if (event != BUS_NOTIFY_ADD_DEVICE) | |
113 | return NOTIFY_DONE; | |
114 | set_dma_ops(dev, &mvebu_hwcc_dma_ops); | |
115 | ||
116 | return NOTIFY_OK; | |
117 | } | |
118 | ||
119 | static struct notifier_block mvebu_hwcc_platform_nb = { | |
120 | .notifier_call = mvebu_hwcc_platform_notifier, | |
121 | }; | |
122 | ||
009f1315 GC |
123 | int __init coherency_init(void) |
124 | { | |
125 | struct device_node *np; | |
126 | ||
127 | np = of_find_matching_node(NULL, of_coherency_table); | |
128 | if (np) { | |
580ff0ee | 129 | struct resource res; |
009f1315 | 130 | pr_info("Initializing Coherency fabric\n"); |
580ff0ee TP |
131 | of_address_to_resource(np, 0, &res); |
132 | coherency_phys_base = res.start; | |
133 | /* | |
134 | * Ensure secondary CPUs will see the updated value, | |
135 | * which they read before they join the coherency | |
136 | * fabric, and therefore before they are coherent with | |
137 | * the boot CPU cache. | |
138 | */ | |
139 | sync_cache_w(&coherency_phys_base); | |
009f1315 | 140 | coherency_base = of_iomap(np, 0); |
e60304f8 GC |
141 | coherency_cpu_base = of_iomap(np, 1); |
142 | set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); | |
009f1315 GC |
143 | } |
144 | ||
145 | return 0; | |
146 | } | |
865e0527 TP |
147 | |
148 | static int __init coherency_late_init(void) | |
149 | { | |
1919bff0 TP |
150 | if (of_find_matching_node(NULL, of_coherency_table)) |
151 | bus_register_notifier(&platform_bus_type, | |
152 | &mvebu_hwcc_platform_nb); | |
865e0527 TP |
153 | return 0; |
154 | } | |
155 | ||
156 | postcore_initcall(coherency_late_init); |