aboutsummaryrefslogtreecommitdiffstats
path: root/arch/microblaze/mm/consistent.c
diff options
context:
space:
mode:
authorMichal Simek <monstr@monstr.eu>2010-02-22 06:16:08 -0500
committerMichal Simek <monstr@monstr.eu>2010-03-11 08:14:43 -0500
commit3a0d7a4dd5b3a6545e5764735b48ab84e64af723 (patch)
treee42c0d8af3695e96729d2434ee386ff00cabaf2d /arch/microblaze/mm/consistent.c
parentae8ee1505162f47f8b8cf7a44c26ea6b172e1445 (diff)
microblaze: Add consistent code
Remove ancient Kconfig option for consistent code. MMU uses cache inhibit pages. noMMU uses UNCACHE SHADOW feature where is used double ram size. For example: Physical ram is 256MB and cache are setup to cover the same size. But if you setup in HW that size is 512MB and cache covers 256MB than you can use adresses from 256-512MB without caches and correspond with 0-256MB with cache. That's why I am using dcache base/high addresses to find out uncache area. Signed-off-by: Michal Simek <monstr@monstr.eu>
Diffstat (limited to 'arch/microblaze/mm/consistent.c')
-rw-r--r--arch/microblaze/mm/consistent.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/arch/microblaze/mm/consistent.c b/arch/microblaze/mm/consistent.c
new file mode 100644
index 000000000000..a9b443e3fb98
--- /dev/null
+++ b/arch/microblaze/mm/consistent.c
@@ -0,0 +1,246 @@
1/*
2 * Microblaze support for cache consistent memory.
3 * Copyright (C) 2010 Michal Simek <monstr@monstr.eu>
4 * Copyright (C) 2010 PetaLogix
5 * Copyright (C) 2005 John Williams <jwilliams@itee.uq.edu.au>
6 *
7 * Based on PowerPC version derived from arch/arm/mm/consistent.c
8 * Copyright (C) 2001 Dan Malek (dmalek@jlc.net)
9 * Copyright (C) 2000 Russell King
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/module.h>
17#include <linux/signal.h>
18#include <linux/sched.h>
19#include <linux/kernel.h>
20#include <linux/errno.h>
21#include <linux/string.h>
22#include <linux/types.h>
23#include <linux/ptrace.h>
24#include <linux/mman.h>
25#include <linux/mm.h>
26#include <linux/swap.h>
27#include <linux/stddef.h>
28#include <linux/vmalloc.h>
29#include <linux/init.h>
30#include <linux/delay.h>
31#include <linux/bootmem.h>
32#include <linux/highmem.h>
33#include <linux/pci.h>
34#include <linux/interrupt.h>
35
36#include <asm/pgalloc.h>
37#include <linux/io.h>
38#include <linux/hardirq.h>
39#include <asm/mmu_context.h>
40#include <asm/mmu.h>
41#include <linux/uaccess.h>
42#include <asm/pgtable.h>
43#include <asm/cpuinfo.h>
44
45#ifndef CONFIG_MMU
46
47/* I have to use dcache values because I can't relate on ram size */
48#define UNCACHED_SHADOW_MASK (cpuinfo.dcache_high - cpuinfo.dcache_base + 1)
49
50/*
51 * Consistent memory allocators. Used for DMA devices that want to
52 * share uncached memory with the processor core.
53 * My crufty no-MMU approach is simple. In the HW platform we can optionally
54 * mirror the DDR up above the processor cacheable region. So, memory accessed
55 * in this mirror region will not be cached. It's alloced from the same
56 * pool as normal memory, but the handle we return is shifted up into the
57 * uncached region. This will no doubt cause big problems if memory allocated
58 * here is not also freed properly. -- JW
59 */
60void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
61{
62 struct page *page, *end, *free;
63 unsigned long order;
64 void *ret, *virt;
65
66 if (in_interrupt())
67 BUG();
68
69 size = PAGE_ALIGN(size);
70 order = get_order(size);
71
72 page = alloc_pages(gfp, order);
73 if (!page)
74 goto no_page;
75
76 /* We could do with a page_to_phys and page_to_bus here. */
77 virt = page_address(page);
78 ret = ioremap(virt_to_phys(virt), size);
79 if (!ret)
80 goto no_remap;
81
82 /*
83 * Here's the magic! Note if the uncached shadow is not implemented,
84 * it's up to the calling code to also test that condition and make
85 * other arranegments, such as manually flushing the cache and so on.
86 */
87#ifdef CONFIG_XILINX_UNCACHED_SHADOW
88 ret = (void *)((unsigned) ret | UNCACHED_SHADOW_MASK);
89#endif
90 /* dma_handle is same as physical (shadowed) address */
91 *dma_handle = (dma_addr_t)ret;
92
93 /*
94 * free wasted pages. We skip the first page since we know
95 * that it will have count = 1 and won't require freeing.
96 * We also mark the pages in use as reserved so that
97 * remap_page_range works.
98 */
99 page = virt_to_page(virt);
100 free = page + (size >> PAGE_SHIFT);
101 end = page + (1 << order);
102
103 for (; page < end; page++) {
104 init_page_count(page);
105 if (page >= free)
106 __free_page(page);
107 else
108 SetPageReserved(page);
109 }
110
111 return ret;
112no_remap:
113 __free_pages(page, order);
114no_page:
115 return NULL;
116}
117
118#else
119
120void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
121{
122 int order, err, i;
123 unsigned long page, va, flags;
124 phys_addr_t pa;
125 struct vm_struct *area;
126 void *ret;
127
128 if (in_interrupt())
129 BUG();
130
131 /* Only allocate page size areas. */
132 size = PAGE_ALIGN(size);
133 order = get_order(size);
134
135 page = __get_free_pages(gfp, order);
136 if (!page) {
137 BUG();
138 return NULL;
139 }
140
141 /*
142 * we need to ensure that there are no cachelines in use,
143 * or worse dirty in this area.
144 */
145 flush_dcache_range(virt_to_phys(page), virt_to_phys(page) + size);
146
147 /* Allocate some common virtual space to map the new pages. */
148 area = get_vm_area(size, VM_ALLOC);
149 if (area == NULL) {
150 free_pages(page, order);
151 return NULL;
152 }
153 va = (unsigned long) area->addr;
154 ret = (void *)va;
155
156 /* This gives us the real physical address of the first page. */
157 *dma_handle = pa = virt_to_bus((void *)page);
158
159 /* MS: This is the whole magic - use cache inhibit pages */
160 flags = _PAGE_KERNEL | _PAGE_NO_CACHE;
161
162 /*
163 * Set refcount=1 on all pages in an order>0
164 * allocation so that vfree() will actually
165 * free all pages that were allocated.
166 */
167 if (order > 0) {
168 struct page *rpage = virt_to_page(page);
169 for (i = 1; i < (1 << order); i++)
170 init_page_count(rpage+i);
171 }
172
173 err = 0;
174 for (i = 0; i < size && err == 0; i += PAGE_SIZE)
175 err = map_page(va+i, pa+i, flags);
176
177 if (err) {
178 vfree((void *)va);
179 return NULL;
180 }
181
182 return ret;
183}
184#endif /* CONFIG_MMU */
185EXPORT_SYMBOL(consistent_alloc);
186
187/*
188 * free page(s) as defined by the above mapping.
189 */
190void consistent_free(void *vaddr)
191{
192 if (in_interrupt())
193 BUG();
194
195 /* Clear SHADOW_MASK bit in address, and free as per usual */
196#ifdef CONFIG_XILINX_UNCACHED_SHADOW
197 vaddr = (void *)((unsigned)vaddr & ~UNCACHED_SHADOW_MASK);
198#endif
199 vfree(vaddr);
200}
201EXPORT_SYMBOL(consistent_free);
202
203/*
204 * make an area consistent.
205 */
206void consistent_sync(void *vaddr, size_t size, int direction)
207{
208 unsigned long start;
209 unsigned long end;
210
211 start = (unsigned long)vaddr;
212
213 /* Convert start address back down to unshadowed memory region */
214#ifdef CONFIG_XILINX_UNCACHED_SHADOW
215 start &= ~UNCACHED_SHADOW_MASK;
216#endif
217 end = start + size;
218
219 switch (direction) {
220 case PCI_DMA_NONE:
221 BUG();
222 case PCI_DMA_FROMDEVICE: /* invalidate only */
223 flush_dcache_range(start, end);
224 break;
225 case PCI_DMA_TODEVICE: /* writeback only */
226 flush_dcache_range(start, end);
227 break;
228 case PCI_DMA_BIDIRECTIONAL: /* writeback and invalidate */
229 flush_dcache_range(start, end);
230 break;
231 }
232}
233EXPORT_SYMBOL(consistent_sync);
234
235/*
236 * consistent_sync_page makes memory consistent. identical
237 * to consistent_sync, but takes a struct page instead of a
238 * virtual address
239 */
240void consistent_sync_page(struct page *page, unsigned long offset,
241 size_t size, int direction)
242{
243 unsigned long start = (unsigned long)page_address(page) + offset;
244 consistent_sync((void *)start, size, direction);
245}
246EXPORT_SYMBOL(consistent_sync_page);