OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [char/] [mspec.c] - Blame information for rev 65

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * Copyright (C) 2001-2006 Silicon Graphics, Inc.  All rights
3
 * reserved.
4
 *
5
 * This program is free software; you can redistribute it and/or modify it
6
 * under the terms of version 2 of the GNU General Public License
7
 * as published by the Free Software Foundation.
8
 */
9
 
10
/*
11
 * SN Platform Special Memory (mspec) Support
12
 *
13
 * This driver exports the SN special memory (mspec) facility to user
14
 * processes.
15
 * There are three types of memory made available thru this driver:
16
 * fetchops, uncached and cached.
17
 *
18
 * Fetchops are atomic memory operations that are implemented in the
19
 * memory controller on SGI SN hardware.
20
 *
21
 * Uncached are used for memory write combining feature of the ia64
22
 * cpu.
23
 *
24
 * Cached are used for areas of memory that are used as cached addresses
25
 * on our partition and used as uncached addresses from other partitions.
26
 * Due to a design constraint of the SN2 Shub, you can not have processors
27
 * on the same FSB perform both a cached and uncached reference to the
28
 * same cache line.  These special memory cached regions prevent the
29
 * kernel from ever dropping in a TLB entry and therefore prevent the
30
 * processor from ever speculating a cache line from this page.
31
 */
32
 
33
#include <linux/types.h>
34
#include <linux/kernel.h>
35
#include <linux/module.h>
36
#include <linux/init.h>
37
#include <linux/errno.h>
38
#include <linux/miscdevice.h>
39
#include <linux/spinlock.h>
40
#include <linux/mm.h>
41
#include <linux/fs.h>
42
#include <linux/vmalloc.h>
43
#include <linux/string.h>
44
#include <linux/slab.h>
45
#include <linux/numa.h>
46
#include <asm/page.h>
47
#include <asm/system.h>
48
#include <asm/pgtable.h>
49
#include <asm/atomic.h>
50
#include <asm/tlbflush.h>
51
#include <asm/uncached.h>
52
#include <asm/sn/addrs.h>
53
#include <asm/sn/arch.h>
54
#include <asm/sn/mspec.h>
55
#include <asm/sn/sn_cpuid.h>
56
#include <asm/sn/io.h>
57
#include <asm/sn/bte.h>
58
#include <asm/sn/shubio.h>
59
 
60
 
61
#define FETCHOP_ID      "SGI Fetchop,"
62
#define CACHED_ID       "Cached,"
63
#define UNCACHED_ID     "Uncached"
64
#define REVISION        "4.0"
65
#define MSPEC_BASENAME  "mspec"
66
 
67
/*
68
 * Page types allocated by the device.
69
 */
70
enum mspec_page_type {
71
        MSPEC_FETCHOP = 1,
72
        MSPEC_CACHED,
73
        MSPEC_UNCACHED
74
};
75
 
76
#ifdef CONFIG_SGI_SN
77
static int is_sn2;
78
#else
79
#define is_sn2          0
80
#endif
81
 
82
/*
83
 * One of these structures is allocated when an mspec region is mmaped. The
84
 * structure is pointed to by the vma->vm_private_data field in the vma struct.
85
 * This structure is used to record the addresses of the mspec pages.
86
 * This structure is shared by all vma's that are split off from the
87
 * original vma when split_vma()'s are done.
88
 *
89
 * The refcnt is incremented atomically because mm->mmap_sem does not
90
 * protect in fork case where multiple tasks share the vma_data.
91
 */
92
struct vma_data {
93
        atomic_t refcnt;        /* Number of vmas sharing the data. */
94
        spinlock_t lock;        /* Serialize access to this structure. */
95
        int count;              /* Number of pages allocated. */
96
        enum mspec_page_type type; /* Type of pages allocated. */
97
        int flags;              /* See VMD_xxx below. */
98
        unsigned long vm_start; /* Original (unsplit) base. */
99
        unsigned long vm_end;   /* Original (unsplit) end. */
100
        unsigned long maddr[0];  /* Array of MSPEC addresses. */
101
};
102
 
103
#define VMD_VMALLOCED 0x1       /* vmalloc'd rather than kmalloc'd */
104
 
105
/* used on shub2 to clear FOP cache in the HUB */
106
static unsigned long scratch_page[MAX_NUMNODES];
107
#define SH2_AMO_CACHE_ENTRIES   4
108
 
109
static inline int
110
mspec_zero_block(unsigned long addr, int len)
111
{
112
        int status;
113
 
114
        if (is_sn2) {
115
                if (is_shub2()) {
116
                        int nid;
117
                        void *p;
118
                        int i;
119
 
120
                        nid = nasid_to_cnodeid(get_node_number(__pa(addr)));
121
                        p = (void *)TO_AMO(scratch_page[nid]);
122
 
123
                        for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) {
124
                                FETCHOP_LOAD_OP(p, FETCHOP_LOAD);
125
                                p += FETCHOP_VAR_SIZE;
126
                        }
127
                }
128
 
129
                status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len,
130
                                  BTE_WACQUIRE | BTE_ZERO_FILL, NULL);
131
        } else {
132
                memset((char *) addr, 0, len);
133
                status = 0;
134
        }
135
        return status;
136
}
137
 
138
/*
139
 * mspec_open
140
 *
141
 * Called when a device mapping is created by a means other than mmap
142
 * (via fork, munmap, etc.).  Increments the reference count on the
143
 * underlying mspec data so it is not freed prematurely.
144
 */
145
static void
146
mspec_open(struct vm_area_struct *vma)
147
{
148
        struct vma_data *vdata;
149
 
150
        vdata = vma->vm_private_data;
151
        atomic_inc(&vdata->refcnt);
152
}
153
 
154
/*
155
 * mspec_close
156
 *
157
 * Called when unmapping a device mapping. Frees all mspec pages
158
 * belonging to all the vma's sharing this vma_data structure.
159
 */
160
static void
161
mspec_close(struct vm_area_struct *vma)
162
{
163
        struct vma_data *vdata;
164
        int index, last_index;
165
        unsigned long my_page;
166
 
167
        vdata = vma->vm_private_data;
168
 
169
        if (!atomic_dec_and_test(&vdata->refcnt))
170
                return;
171
 
172
        last_index = (vdata->vm_end - vdata->vm_start) >> PAGE_SHIFT;
173
        for (index = 0; index < last_index; index++) {
174
                if (vdata->maddr[index] == 0)
175
                        continue;
176
                /*
177
                 * Clear the page before sticking it back
178
                 * into the pool.
179
                 */
180
                my_page = vdata->maddr[index];
181
                vdata->maddr[index] = 0;
182
                if (!mspec_zero_block(my_page, PAGE_SIZE))
183
                        uncached_free_page(my_page);
184
                else
185
                        printk(KERN_WARNING "mspec_close(): "
186
                               "failed to zero page %ld\n", my_page);
187
        }
188
 
189
        if (vdata->flags & VMD_VMALLOCED)
190
                vfree(vdata);
191
        else
192
                kfree(vdata);
193
}
194
 
195
/*
196
 * mspec_nopfn
197
 *
198
 * Creates a mspec page and maps it to user space.
199
 */
200
static unsigned long
201
mspec_nopfn(struct vm_area_struct *vma, unsigned long address)
202
{
203
        unsigned long paddr, maddr;
204
        unsigned long pfn;
205
        int index;
206
        struct vma_data *vdata = vma->vm_private_data;
207
 
208
        BUG_ON(address < vdata->vm_start || address >= vdata->vm_end);
209
        index = (address - vdata->vm_start) >> PAGE_SHIFT;
210
        maddr = (volatile unsigned long) vdata->maddr[index];
211
        if (maddr == 0) {
212
                maddr = uncached_alloc_page(numa_node_id());
213
                if (maddr == 0)
214
                        return NOPFN_OOM;
215
 
216
                spin_lock(&vdata->lock);
217
                if (vdata->maddr[index] == 0) {
218
                        vdata->count++;
219
                        vdata->maddr[index] = maddr;
220
                } else {
221
                        uncached_free_page(maddr);
222
                        maddr = vdata->maddr[index];
223
                }
224
                spin_unlock(&vdata->lock);
225
        }
226
 
227
        if (vdata->type == MSPEC_FETCHOP)
228
                paddr = TO_AMO(maddr);
229
        else
230
                paddr = maddr & ~__IA64_UNCACHED_OFFSET;
231
 
232
        pfn = paddr >> PAGE_SHIFT;
233
 
234
        return pfn;
235
}
236
 
237
static struct vm_operations_struct mspec_vm_ops = {
238
        .open = mspec_open,
239
        .close = mspec_close,
240
        .nopfn = mspec_nopfn
241
};
242
 
243
/*
244
 * mspec_mmap
245
 *
246
 * Called when mmaping the device.  Initializes the vma with a fault handler
247
 * and private data structure necessary to allocate, track, and free the
248
 * underlying pages.
249
 */
250
static int
251
mspec_mmap(struct file *file, struct vm_area_struct *vma,
252
                                        enum mspec_page_type type)
253
{
254
        struct vma_data *vdata;
255
        int pages, vdata_size, flags = 0;
256
 
257
        if (vma->vm_pgoff != 0)
258
                return -EINVAL;
259
 
260
        if ((vma->vm_flags & VM_SHARED) == 0)
261
                return -EINVAL;
262
 
263
        if ((vma->vm_flags & VM_WRITE) == 0)
264
                return -EPERM;
265
 
266
        pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
267
        vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
268
        if (vdata_size <= PAGE_SIZE)
269
                vdata = kmalloc(vdata_size, GFP_KERNEL);
270
        else {
271
                vdata = vmalloc(vdata_size);
272
                flags = VMD_VMALLOCED;
273
        }
274
        if (!vdata)
275
                return -ENOMEM;
276
        memset(vdata, 0, vdata_size);
277
 
278
        vdata->vm_start = vma->vm_start;
279
        vdata->vm_end = vma->vm_end;
280
        vdata->flags = flags;
281
        vdata->type = type;
282
        spin_lock_init(&vdata->lock);
283
        vdata->refcnt = ATOMIC_INIT(1);
284
        vma->vm_private_data = vdata;
285
 
286
        vma->vm_flags |= (VM_IO | VM_RESERVED | VM_PFNMAP);
287
        if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED)
288
                vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
289
        vma->vm_ops = &mspec_vm_ops;
290
 
291
        return 0;
292
}
293
 
294
static int
295
fetchop_mmap(struct file *file, struct vm_area_struct *vma)
296
{
297
        return mspec_mmap(file, vma, MSPEC_FETCHOP);
298
}
299
 
300
static int
301
cached_mmap(struct file *file, struct vm_area_struct *vma)
302
{
303
        return mspec_mmap(file, vma, MSPEC_CACHED);
304
}
305
 
306
static int
307
uncached_mmap(struct file *file, struct vm_area_struct *vma)
308
{
309
        return mspec_mmap(file, vma, MSPEC_UNCACHED);
310
}
311
 
312
static const struct file_operations fetchop_fops = {
313
        .owner = THIS_MODULE,
314
        .mmap = fetchop_mmap
315
};
316
 
317
static struct miscdevice fetchop_miscdev = {
318
        .minor = MISC_DYNAMIC_MINOR,
319
        .name = "sgi_fetchop",
320
        .fops = &fetchop_fops
321
};
322
 
323
static const struct file_operations cached_fops = {
324
        .owner = THIS_MODULE,
325
        .mmap = cached_mmap
326
};
327
 
328
static struct miscdevice cached_miscdev = {
329
        .minor = MISC_DYNAMIC_MINOR,
330
        .name = "mspec_cached",
331
        .fops = &cached_fops
332
};
333
 
334
static const struct file_operations uncached_fops = {
335
        .owner = THIS_MODULE,
336
        .mmap = uncached_mmap
337
};
338
 
339
static struct miscdevice uncached_miscdev = {
340
        .minor = MISC_DYNAMIC_MINOR,
341
        .name = "mspec_uncached",
342
        .fops = &uncached_fops
343
};
344
 
345
/*
346
 * mspec_init
347
 *
348
 * Called at boot time to initialize the mspec facility.
349
 */
350
static int __init
351
mspec_init(void)
352
{
353
        int ret;
354
        int nid;
355
 
356
        /*
357
         * The fetchop device only works on SN2 hardware, uncached and cached
358
         * memory drivers should both be valid on all ia64 hardware
359
         */
360
#ifdef CONFIG_SGI_SN
361
        if (ia64_platform_is("sn2")) {
362
                is_sn2 = 1;
363
                if (is_shub2()) {
364
                        ret = -ENOMEM;
365
                        for_each_node_state(nid, N_ONLINE) {
366
                                int actual_nid;
367
                                int nasid;
368
                                unsigned long phys;
369
 
370
                                scratch_page[nid] = uncached_alloc_page(nid);
371
                                if (scratch_page[nid] == 0)
372
                                        goto free_scratch_pages;
373
                                phys = __pa(scratch_page[nid]);
374
                                nasid = get_node_number(phys);
375
                                actual_nid = nasid_to_cnodeid(nasid);
376
                                if (actual_nid != nid)
377
                                        goto free_scratch_pages;
378
                        }
379
                }
380
 
381
                ret = misc_register(&fetchop_miscdev);
382
                if (ret) {
383
                        printk(KERN_ERR
384
                               "%s: failed to register device %i\n",
385
                               FETCHOP_ID, ret);
386
                        goto free_scratch_pages;
387
                }
388
        }
389
#endif
390
        ret = misc_register(&cached_miscdev);
391
        if (ret) {
392
                printk(KERN_ERR "%s: failed to register device %i\n",
393
                       CACHED_ID, ret);
394
                if (is_sn2)
395
                        misc_deregister(&fetchop_miscdev);
396
                goto free_scratch_pages;
397
        }
398
        ret = misc_register(&uncached_miscdev);
399
        if (ret) {
400
                printk(KERN_ERR "%s: failed to register device %i\n",
401
                       UNCACHED_ID, ret);
402
                misc_deregister(&cached_miscdev);
403
                if (is_sn2)
404
                        misc_deregister(&fetchop_miscdev);
405
                goto free_scratch_pages;
406
        }
407
 
408
        printk(KERN_INFO "%s %s initialized devices: %s %s %s\n",
409
               MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "",
410
               CACHED_ID, UNCACHED_ID);
411
 
412
        return 0;
413
 
414
 free_scratch_pages:
415
        for_each_node(nid) {
416
                if (scratch_page[nid] != 0)
417
                        uncached_free_page(scratch_page[nid]);
418
        }
419
        return ret;
420
}
421
 
422
static void __exit
423
mspec_exit(void)
424
{
425
        int nid;
426
 
427
        misc_deregister(&uncached_miscdev);
428
        misc_deregister(&cached_miscdev);
429
        if (is_sn2) {
430
                misc_deregister(&fetchop_miscdev);
431
 
432
                for_each_node(nid) {
433
                        if (scratch_page[nid] != 0)
434
                                uncached_free_page(scratch_page[nid]);
435
                }
436
        }
437
}
438
 
439
module_init(mspec_init);
440
module_exit(mspec_exit);
441
 
442
MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>");
443
MODULE_DESCRIPTION("Driver for SGI SN special memory operations");
444
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.