xref: /openbmc/linux/arch/ia64/kernel/uncached.c (revision 386d1d50c8eef254653b1015fde06622ef38ba76)
1f14f75b8SJes Sorensen /*
2f14f75b8SJes Sorensen  * Copyright (C) 2001-2005 Silicon Graphics, Inc.  All rights reserved.
3f14f75b8SJes Sorensen  *
4f14f75b8SJes Sorensen  * This program is free software; you can redistribute it and/or modify it
5f14f75b8SJes Sorensen  * under the terms of version 2 of the GNU General Public License
6f14f75b8SJes Sorensen  * as published by the Free Software Foundation.
7f14f75b8SJes Sorensen  *
8f14f75b8SJes Sorensen  * A simple uncached page allocator using the generic allocator. This
9f14f75b8SJes Sorensen  * allocator first utilizes the spare (spill) pages found in the EFI
10f14f75b8SJes Sorensen  * memmap and will then start converting cached pages to uncached ones
11f14f75b8SJes Sorensen  * at a granule at a time. Node awareness is implemented by having a
12f14f75b8SJes Sorensen  * pool of pages per node.
13f14f75b8SJes Sorensen  */
14f14f75b8SJes Sorensen 
15f14f75b8SJes Sorensen #include <linux/types.h>
16f14f75b8SJes Sorensen #include <linux/kernel.h>
17f14f75b8SJes Sorensen #include <linux/module.h>
18f14f75b8SJes Sorensen #include <linux/init.h>
19f14f75b8SJes Sorensen #include <linux/errno.h>
20f14f75b8SJes Sorensen #include <linux/string.h>
21f14f75b8SJes Sorensen #include <linux/slab.h>
22f14f75b8SJes Sorensen #include <linux/efi.h>
23f14f75b8SJes Sorensen #include <linux/genalloc.h>
24f14f75b8SJes Sorensen #include <asm/page.h>
25f14f75b8SJes Sorensen #include <asm/pal.h>
26f14f75b8SJes Sorensen #include <asm/system.h>
27f14f75b8SJes Sorensen #include <asm/pgtable.h>
28f14f75b8SJes Sorensen #include <asm/atomic.h>
29f14f75b8SJes Sorensen #include <asm/tlbflush.h>
30f14f75b8SJes Sorensen #include <asm/sn/arch.h>
31f14f75b8SJes Sorensen 
32f14f75b8SJes Sorensen #define DEBUG	0
33f14f75b8SJes Sorensen 
34f14f75b8SJes Sorensen #if DEBUG
35f14f75b8SJes Sorensen #define dprintk			printk
36f14f75b8SJes Sorensen #else
37f14f75b8SJes Sorensen #define dprintk(x...)		do { } while (0)
38f14f75b8SJes Sorensen #endif
39f14f75b8SJes Sorensen 
40f14f75b8SJes Sorensen void __init efi_memmap_walk_uc (efi_freemem_callback_t callback);
41f14f75b8SJes Sorensen 
42f14f75b8SJes Sorensen #define MAX_UNCACHED_GRANULES	5
43f14f75b8SJes Sorensen static int allocated_granules;
44f14f75b8SJes Sorensen 
45f14f75b8SJes Sorensen struct gen_pool *uncached_pool[MAX_NUMNODES];
46f14f75b8SJes Sorensen 
47f14f75b8SJes Sorensen 
48f14f75b8SJes Sorensen static void uncached_ipi_visibility(void *data)
49f14f75b8SJes Sorensen {
50f14f75b8SJes Sorensen 	int status;
51f14f75b8SJes Sorensen 
52f14f75b8SJes Sorensen 	status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL);
53f14f75b8SJes Sorensen 	if ((status != PAL_VISIBILITY_OK) &&
54f14f75b8SJes Sorensen 	    (status != PAL_VISIBILITY_OK_REMOTE_NEEDED))
55f14f75b8SJes Sorensen 		printk(KERN_DEBUG "pal_prefetch_visibility() returns %i on "
563bd7f017SJes Sorensen 		       "CPU %i\n", status, raw_smp_processor_id());
57f14f75b8SJes Sorensen }
58f14f75b8SJes Sorensen 
59f14f75b8SJes Sorensen 
60f14f75b8SJes Sorensen static void uncached_ipi_mc_drain(void *data)
61f14f75b8SJes Sorensen {
62f14f75b8SJes Sorensen 	int status;
63f14f75b8SJes Sorensen 	status = ia64_pal_mc_drain();
64f14f75b8SJes Sorensen 	if (status)
65f14f75b8SJes Sorensen 		printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on "
663bd7f017SJes Sorensen 		       "CPU %i\n", status, raw_smp_processor_id());
67f14f75b8SJes Sorensen }
68f14f75b8SJes Sorensen 
69f14f75b8SJes Sorensen 
70f14f75b8SJes Sorensen static unsigned long
71f14f75b8SJes Sorensen uncached_get_new_chunk(struct gen_pool *poolp)
72f14f75b8SJes Sorensen {
73f14f75b8SJes Sorensen 	struct page *page;
74f14f75b8SJes Sorensen 	void *tmp;
75f14f75b8SJes Sorensen 	int status, i;
76f14f75b8SJes Sorensen 	unsigned long addr, node;
77f14f75b8SJes Sorensen 
78f14f75b8SJes Sorensen 	if (allocated_granules >= MAX_UNCACHED_GRANULES)
79f14f75b8SJes Sorensen 		return 0;
80f14f75b8SJes Sorensen 
81f14f75b8SJes Sorensen 	node = poolp->private;
82f14f75b8SJes Sorensen 	page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO,
83f14f75b8SJes Sorensen 				IA64_GRANULE_SHIFT-PAGE_SHIFT);
84f14f75b8SJes Sorensen 
85f14f75b8SJes Sorensen 	dprintk(KERN_INFO "get_new_chunk page %p, addr %lx\n",
86f14f75b8SJes Sorensen 		page, (unsigned long)(page-vmem_map) << PAGE_SHIFT);
87f14f75b8SJes Sorensen 
88f14f75b8SJes Sorensen 	/*
89f14f75b8SJes Sorensen 	 * Do magic if no mem on local node! XXX
90f14f75b8SJes Sorensen 	 */
91f14f75b8SJes Sorensen 	if (!page)
92f14f75b8SJes Sorensen 		return 0;
93f14f75b8SJes Sorensen 	tmp = page_address(page);
94f14f75b8SJes Sorensen 
95f14f75b8SJes Sorensen 	/*
96f14f75b8SJes Sorensen 	 * There's a small race here where it's possible for someone to
97f14f75b8SJes Sorensen 	 * access the page through /dev/mem halfway through the conversion
98f14f75b8SJes Sorensen 	 * to uncached - not sure it's really worth bothering about
99f14f75b8SJes Sorensen 	 */
100f14f75b8SJes Sorensen 	for (i = 0; i < (IA64_GRANULE_SIZE / PAGE_SIZE); i++)
101f14f75b8SJes Sorensen 		SetPageUncached(&page[i]);
102f14f75b8SJes Sorensen 
103f14f75b8SJes Sorensen 	flush_tlb_kernel_range(tmp, tmp + IA64_GRANULE_SIZE);
104f14f75b8SJes Sorensen 
105f14f75b8SJes Sorensen 	status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL);
106f14f75b8SJes Sorensen 
107f14f75b8SJes Sorensen 	dprintk(KERN_INFO "pal_prefetch_visibility() returns %i on cpu %i\n",
1083bd7f017SJes Sorensen 		status, raw_smp_processor_id());
109f14f75b8SJes Sorensen 
110f14f75b8SJes Sorensen 	if (!status) {
111f14f75b8SJes Sorensen 		status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1);
112f14f75b8SJes Sorensen 		if (status)
113f14f75b8SJes Sorensen 			printk(KERN_WARNING "smp_call_function failed for "
114f14f75b8SJes Sorensen 			       "uncached_ipi_visibility! (%i)\n", status);
115f14f75b8SJes Sorensen 	}
116f14f75b8SJes Sorensen 
117f14f75b8SJes Sorensen 	if (ia64_platform_is("sn2"))
118f14f75b8SJes Sorensen 		sn_flush_all_caches((unsigned long)tmp, IA64_GRANULE_SIZE);
119f14f75b8SJes Sorensen 	else
120f14f75b8SJes Sorensen 		flush_icache_range((unsigned long)tmp,
121f14f75b8SJes Sorensen 				   (unsigned long)tmp+IA64_GRANULE_SIZE);
122f14f75b8SJes Sorensen 
123f14f75b8SJes Sorensen 	ia64_pal_mc_drain();
124f14f75b8SJes Sorensen 	status = smp_call_function(uncached_ipi_mc_drain, NULL, 0, 1);
125f14f75b8SJes Sorensen 	if (status)
126f14f75b8SJes Sorensen 		printk(KERN_WARNING "smp_call_function failed for "
127f14f75b8SJes Sorensen 		       "uncached_ipi_mc_drain! (%i)\n", status);
128f14f75b8SJes Sorensen 
129f14f75b8SJes Sorensen 	addr = (unsigned long)tmp - PAGE_OFFSET + __IA64_UNCACHED_OFFSET;
130f14f75b8SJes Sorensen 
131f14f75b8SJes Sorensen 	allocated_granules++;
132f14f75b8SJes Sorensen 	return addr;
133f14f75b8SJes Sorensen }
134f14f75b8SJes Sorensen 
135f14f75b8SJes Sorensen 
136f14f75b8SJes Sorensen /*
137f14f75b8SJes Sorensen  * uncached_alloc_page
138f14f75b8SJes Sorensen  *
139f14f75b8SJes Sorensen  * Allocate 1 uncached page. Allocates on the requested node. If no
140f14f75b8SJes Sorensen  * uncached pages are available on the requested node, roundrobin starting
141f14f75b8SJes Sorensen  * with higher nodes.
142f14f75b8SJes Sorensen  */
143f14f75b8SJes Sorensen unsigned long
144f14f75b8SJes Sorensen uncached_alloc_page(int nid)
145f14f75b8SJes Sorensen {
146f14f75b8SJes Sorensen 	unsigned long maddr;
147f14f75b8SJes Sorensen 
148f14f75b8SJes Sorensen 	maddr = gen_pool_alloc(uncached_pool[nid], PAGE_SIZE);
149f14f75b8SJes Sorensen 
150f14f75b8SJes Sorensen 	dprintk(KERN_DEBUG "uncached_alloc_page returns %lx on node %i\n",
151f14f75b8SJes Sorensen 		maddr, nid);
152f14f75b8SJes Sorensen 
153f14f75b8SJes Sorensen 	/*
154f14f75b8SJes Sorensen 	 * If no memory is availble on our local node, try the
155f14f75b8SJes Sorensen 	 * remaining nodes in the system.
156f14f75b8SJes Sorensen 	 */
157f14f75b8SJes Sorensen 	if (!maddr) {
158f14f75b8SJes Sorensen 		int i;
159f14f75b8SJes Sorensen 
160f14f75b8SJes Sorensen 		for (i = MAX_NUMNODES - 1; i >= 0; i--) {
161f14f75b8SJes Sorensen 			if (i == nid || !node_online(i))
162f14f75b8SJes Sorensen 				continue;
163f14f75b8SJes Sorensen 			maddr = gen_pool_alloc(uncached_pool[i], PAGE_SIZE);
164f14f75b8SJes Sorensen 			dprintk(KERN_DEBUG "uncached_alloc_page alternate search "
165f14f75b8SJes Sorensen 				"returns %lx on node %i\n", maddr, i);
166f14f75b8SJes Sorensen 			if (maddr) {
167f14f75b8SJes Sorensen 				break;
168f14f75b8SJes Sorensen 			}
169f14f75b8SJes Sorensen 		}
170f14f75b8SJes Sorensen 	}
171f14f75b8SJes Sorensen 
172f14f75b8SJes Sorensen 	return maddr;
173f14f75b8SJes Sorensen }
174f14f75b8SJes Sorensen EXPORT_SYMBOL(uncached_alloc_page);
175f14f75b8SJes Sorensen 
176f14f75b8SJes Sorensen 
177f14f75b8SJes Sorensen /*
178f14f75b8SJes Sorensen  * uncached_free_page
179f14f75b8SJes Sorensen  *
180f14f75b8SJes Sorensen  * Free a single uncached page.
181f14f75b8SJes Sorensen  */
182f14f75b8SJes Sorensen void
183f14f75b8SJes Sorensen uncached_free_page(unsigned long maddr)
184f14f75b8SJes Sorensen {
185f14f75b8SJes Sorensen 	int node;
186f14f75b8SJes Sorensen 
187a994018aSMartin Hicks 	node = paddr_to_nid(maddr - __IA64_UNCACHED_OFFSET);
188f14f75b8SJes Sorensen 
189f14f75b8SJes Sorensen 	dprintk(KERN_DEBUG "uncached_free_page(%lx) on node %i\n", maddr, node);
190f14f75b8SJes Sorensen 
191f14f75b8SJes Sorensen 	if ((maddr & (0XFUL << 60)) != __IA64_UNCACHED_OFFSET)
192f14f75b8SJes Sorensen 		panic("uncached_free_page invalid address %lx\n", maddr);
193f14f75b8SJes Sorensen 
194f14f75b8SJes Sorensen 	gen_pool_free(uncached_pool[node], maddr, PAGE_SIZE);
195f14f75b8SJes Sorensen }
196f14f75b8SJes Sorensen EXPORT_SYMBOL(uncached_free_page);
197f14f75b8SJes Sorensen 
198f14f75b8SJes Sorensen 
199f14f75b8SJes Sorensen /*
200f14f75b8SJes Sorensen  * uncached_build_memmap,
201f14f75b8SJes Sorensen  *
202f14f75b8SJes Sorensen  * Called at boot time to build a map of pages that can be used for
203f14f75b8SJes Sorensen  * memory special operations.
204f14f75b8SJes Sorensen  */
205f14f75b8SJes Sorensen static int __init
206f14f75b8SJes Sorensen uncached_build_memmap(unsigned long start, unsigned long end, void *arg)
207f14f75b8SJes Sorensen {
208d8c97d5fSTony Luck 	long length = end - start;
209f14f75b8SJes Sorensen 	int node;
210f14f75b8SJes Sorensen 
211f14f75b8SJes Sorensen 	dprintk(KERN_ERR "uncached_build_memmap(%lx %lx)\n", start, end);
212f14f75b8SJes Sorensen 
213*386d1d50SJohn Hawkes 	touch_softlockup_watchdog();
214d8c97d5fSTony Luck 	memset((char *)start, 0, length);
215f14f75b8SJes Sorensen 
216d8c97d5fSTony Luck 	node = paddr_to_nid(start - __IA64_UNCACHED_OFFSET);
217f14f75b8SJes Sorensen 
218d8c97d5fSTony Luck 	for (; start < end ; start += PAGE_SIZE) {
219d8c97d5fSTony Luck 		dprintk(KERN_INFO "sticking %lx into the pool!\n", start);
220d8c97d5fSTony Luck 		gen_pool_free(uncached_pool[node], start, PAGE_SIZE);
221f14f75b8SJes Sorensen 	}
222f14f75b8SJes Sorensen 
223f14f75b8SJes Sorensen 	return 0;
224f14f75b8SJes Sorensen }
225f14f75b8SJes Sorensen 
226f14f75b8SJes Sorensen 
227f14f75b8SJes Sorensen static int __init uncached_init(void) {
228f14f75b8SJes Sorensen 	int i;
229f14f75b8SJes Sorensen 
230f14f75b8SJes Sorensen 	for (i = 0; i < MAX_NUMNODES; i++) {
231f14f75b8SJes Sorensen 		if (!node_online(i))
232f14f75b8SJes Sorensen 			continue;
233f14f75b8SJes Sorensen 		uncached_pool[i] = gen_pool_create(0, IA64_GRANULE_SHIFT,
234f14f75b8SJes Sorensen 						   &uncached_get_new_chunk, i);
235f14f75b8SJes Sorensen 	}
236f14f75b8SJes Sorensen 
237f14f75b8SJes Sorensen 	efi_memmap_walk_uc(uncached_build_memmap);
238f14f75b8SJes Sorensen 
239f14f75b8SJes Sorensen 	return 0;
240f14f75b8SJes Sorensen }
241f14f75b8SJes Sorensen 
242f14f75b8SJes Sorensen __initcall(uncached_init);
243