Merge with /shiny/git/linux-2.6/.git
[linux-2.6] / arch / cris / arch-v32 / mm / intmem.c
1 /*
2  * Simple allocator for internal RAM in ETRAX FS
3  *
4  * Copyright (c) 2004 Axis Communications AB.
5  */
6
7 #include <linux/list.h>
8 #include <linux/slab.h>
9 #include <asm/io.h>
10 #include <asm/arch/memmap.h>
11
12 #define STATUS_FREE 0
13 #define STATUS_ALLOCATED 1
14
15 struct intmem_allocation {
16         struct list_head entry;
17         unsigned int size;
18         unsigned offset;
19         char status;
20 };
21
22
23 static struct list_head intmem_allocations;
24 static void* intmem_virtual;
25
26 static void crisv32_intmem_init(void)
27 {
28         static int initiated = 0;
29         if (!initiated) {
30                 struct intmem_allocation* alloc =
31                   (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL);
32                 INIT_LIST_HEAD(&intmem_allocations);
33                 intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE);
34                 initiated = 1;
35                 alloc->size = MEM_INTMEM_SIZE;
36                 alloc->offset = 0;
37                 alloc->status = STATUS_FREE;
38                 list_add_tail(&alloc->entry, &intmem_allocations);
39         }
40 }
41
42 void* crisv32_intmem_alloc(unsigned size, unsigned align)
43 {
44         struct intmem_allocation* allocation;
45         struct intmem_allocation* tmp;
46         void* ret = NULL;
47
48         preempt_disable();
49         crisv32_intmem_init();
50
51         list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
52                 int alignment = allocation->offset % align;
53                 alignment = alignment ? align - alignment : alignment;
54
55                 if (allocation->status == STATUS_FREE &&
56                     allocation->size >= size + alignment) {
57                         if (allocation->size > size + alignment) {
58                                 struct intmem_allocation* alloc =
59                                         (struct intmem_allocation*)
60                                         kmalloc(sizeof *alloc, GFP_ATOMIC);
61                                 alloc->status = STATUS_FREE;
62                                 alloc->size = allocation->size - size - alignment;
63                                 alloc->offset = allocation->offset + size;
64                                 list_add(&alloc->entry, &allocation->entry);
65
66                                 if (alignment) {
67                                         struct intmem_allocation* tmp;
68                                         tmp = (struct intmem_allocation*)
69                                                 kmalloc(sizeof *tmp, GFP_ATOMIC);
70                                         tmp->offset = allocation->offset;
71                                         tmp->size = alignment;
72                                         tmp->status = STATUS_FREE;
73                                         allocation->offset += alignment;
74                                         list_add_tail(&tmp->entry, &allocation->entry);
75                                 }
76                         }
77                         allocation->status = STATUS_ALLOCATED;
78                         allocation->size = size;
79                         ret = (void*)((int)intmem_virtual + allocation->offset);
80                 }
81         }
82         preempt_enable();
83         return ret;
84 }
85
86 void crisv32_intmem_free(void* addr)
87 {
88         struct intmem_allocation* allocation;
89         struct intmem_allocation* tmp;
90
91         if (addr == NULL)
92                 return;
93
94         preempt_disable();
95         crisv32_intmem_init();
96
97         list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
98                 if (allocation->offset == (int)(addr - intmem_virtual)) {
99                         struct intmem_allocation* prev =
100                           list_entry(allocation->entry.prev,
101                                      struct intmem_allocation, entry);
102                         struct intmem_allocation* next =
103                           list_entry(allocation->entry.next,
104                                      struct intmem_allocation, entry);
105
106                         allocation->status = STATUS_FREE;
107                         /* Join with prev and/or next if also free */
108                         if (prev->status == STATUS_FREE) {
109                                 prev->size += allocation->size;
110                                 list_del(&allocation->entry);
111                                 kfree(allocation);
112                                 allocation = prev;
113                         }
114                         if (next->status == STATUS_FREE) {
115                                 allocation->size += next->size;
116                                 list_del(&next->entry);
117                                 kfree(next);
118                         }
119                         preempt_enable();
120                         return;
121                 }
122         }
123         preempt_enable();
124 }
125
126 void* crisv32_intmem_phys_to_virt(unsigned long addr)
127 {
128         return (void*)(addr - MEM_INTMEM_START+
129                        (unsigned long)intmem_virtual);
130 }
131
132 unsigned long crisv32_intmem_virt_to_phys(void* addr)
133 {
134         return (unsigned long)((unsigned long )addr -
135           (unsigned long)intmem_virtual + MEM_INTMEM_START);
136 }
137
138
139