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