Staging: dt3155: allocator.c: sparse cleanups
[safe/jmp/linux-2.6] / drivers / staging / dt3155 / allocator.c
1 /*
2  * allocator.c -- allocate after high_memory, if available
3  *
4  * NOTE: this is different from my previous allocator, the one that
5  *       assembles pages, which revealed itself both slow and unreliable.
6  *
7  * Copyright (C) 1998   rubini@linux.it (Alessandro Rubini)
8  *
9  *   This program is free software; you can redistribute it and/or modify
10  *   it under the terms of the GNU General Public License as published by
11  *   the Free Software Foundation; either version 2 of the License, or
12  *   (at your option) any later version.
13  *
14  *   This program is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with this program; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23
24 -- Changes --
25
26   Date        Programmer  Description of changes made
27   -------------------------------------------------------------------
28   02-Aug-2002 NJC         allocator now steps in 1MB increments, rather
29                           than doubling its size each time.
30                           Also, allocator_init(u32 *) now returns
31                           (in the first arg) the size of the free
32                           space.  This is no longer consistent with
33                           using the allocator as a module, and some changes
34                           may be necessary for that purpose.  This was
35                           designed to work with the DT3155 driver, in
36                           stand alone mode only!!!
37   26-Oct-2009 SS          Port to 2.6.30 kernel.
38  */
39
40
41 #ifndef __KERNEL__
42 #  define __KERNEL__
43 #endif
44 #ifndef MODULE
45 #  define MODULE
46 #endif
47
48
49 #include <linux/sched.h>
50 #include <linux/kernel.h>
51 #include <linux/fs.h>
52 #include <linux/proc_fs.h>
53 #include <linux/errno.h>
54 #include <linux/types.h>
55 #include <linux/mm.h>   /* PAGE_ALIGN() */
56 #include <linux/io.h>
57 #include <linux/slab.h>
58
59 #include <asm/page.h>
60
61 #include "allocator.h"
62
63 /*#define ALL_DEBUG*/
64 #define ALL_MSG "allocator: "
65
66 #undef PDEBUG             /* undef it, just in case */
67 #ifdef ALL_DEBUG
68 #  define __static
69 #  define DUMP_LIST() dump_list()
70 #  ifdef __KERNEL__
71      /* This one if debugging is on, and kernel space */
72 #    define PDEBUG(fmt, args...) printk(KERN_DEBUG ALL_MSG fmt, ## args)
73 #  else
74      /* This one for user space */
75 #    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
76 #  endif
77 #else
78 #  define PDEBUG(fmt, args...) /* not debugging: nothing */
79 #  define DUMP_LIST()
80 #  define __static static
81 #endif
82
83 #undef PDEBUGG
84 #define PDEBUGG(fmt, args...)
85 /*#define PDEBUGG(fmt, args...) printk( KERN_DEBUG ALL_MSG fmt, ## args)*/
86
87
88 static int allocator_himem = 1; /* 0 = probe, pos. = megs, neg. = disable   */
89 static int allocator_step = 1;  /* This is the step size in MB              */
90 static int allocator_probe = 1; /* This is a flag -- 1=probe, 0=don't probe */
91
92 static unsigned long allocator_buffer;          /* physical address */
93 static unsigned long allocator_buffer_size;     /* kilobytes */
94
95 /*
96  * The allocator keeps a list of DMA areas, so multiple devices
97  * can coexist. The list is kept sorted by address
98  */
99
100 struct allocator_struct {
101         unsigned long address;
102         unsigned long size;
103         struct allocator_struct *next;
104 };
105
106 static struct allocator_struct *allocator_list;
107
108 #ifdef ALL_DEBUG
109 static int dump_list(void)
110 {
111         struct allocator_struct *ptr;
112
113         PDEBUG("Current list:\n");
114         for (ptr = allocator_list; ptr; ptr = ptr->next)
115                 PDEBUG("0x%08lx (size %likB)\n", ptr->address, ptr->size>>10);
116         return 0;
117 }
118 #endif
119
120 /* ========================================================================
121  * This function is the actual allocator.
122  *
123  * If space is available in high memory (as detected at load time), that
124  * one is returned. The return value is a physical address (i.e., it can
125  * be used straight ahead for DMA, but needs remapping for program use).
126  */
127
128 unsigned long allocator_allocate_dma(unsigned long kilobytes, gfp_t flags)
129 {
130         struct allocator_struct *ptr = allocator_list, *newptr;
131         unsigned long bytes = kilobytes << 10;
132
133         /* check if high memory is available */
134         if (!allocator_buffer)
135                 return 0;
136
137         /* Round it to a multiple of the pagesize */
138         bytes = PAGE_ALIGN(bytes);
139         PDEBUG("request for %li bytes\n", bytes);
140
141         while (ptr && ptr->next) {
142                 if (ptr->next->address - (ptr->address + ptr->size) >= bytes)
143                         break; /* enough space */
144                 ptr = ptr->next;
145         }
146         if (!ptr->next) {
147                 DUMP_LIST();
148                 PDEBUG("alloc failed\n");
149                 return 0; /* end of list */
150         }
151         newptr = kmalloc(sizeof(struct allocator_struct), flags);
152         if (!newptr)
153                 return 0;
154
155         /* ok, now stick it after ptr */
156         newptr->address = ptr->address + ptr->size;
157         newptr->size = bytes;
158         newptr->next = ptr->next;
159         ptr->next = newptr;
160
161         DUMP_LIST();
162         PDEBUG("returning 0x%08lx\n", newptr->address);
163         return newptr->address;
164 }
165
166 int allocator_free_dma(unsigned long address)
167 {
168         struct allocator_struct *ptr = allocator_list, *prev;
169
170         while (ptr && ptr->next) {
171                 if (ptr->next->address == address)
172                         break;
173                 ptr = ptr->next;
174         }
175         /* the one being freed is ptr->next */
176         prev = ptr; ptr = ptr->next;
177
178         if (!ptr) {
179                 printk(KERN_ERR ALL_MSG
180                         "free_dma(0x%08lx) but add. not allocated\n",
181                         ptr->address);
182                 return -EINVAL;
183         }
184         PDEBUGG("freeing: %08lx (%li) next %08lx\n", ptr->address, ptr->size,
185                 ptr->next->address);
186         prev->next = ptr->next;
187         kfree(ptr);
188
189         /* dump_list(); */
190         return 0;
191 }
192
193 /* ========================================================================
194  * Init and cleanup
195  *
196  * On cleanup everything is released. If the list is not empty, that a
197  * problem of our clients
198  */
199 int allocator_init(u32 *allocator_max)
200 {
201         /* check how much free memory is there */
202         void *remapped;
203         unsigned long max;
204         unsigned long trial_size = allocator_himem<<20;
205         unsigned long last_trial = 0;
206         unsigned long step = allocator_step<<20;
207         unsigned long i = 0;
208         struct allocator_struct *head, *tail;
209         char test_string[] = "0123456789abcde"; /* 16 bytes */
210
211         PDEBUGG("himem = %i\n", allocator_himem);
212         if (allocator_himem < 0) /* don't even try */
213                 return -EINVAL;
214
215         if (!trial_size)
216                 trial_size = 1<<20; /* not specified: try one meg */
217
218         while (1) {
219                 remapped = ioremap(__pa(high_memory), trial_size);
220                 if (!remapped) {
221                         PDEBUGG("%li megs failed!\n", trial_size>>20);
222                         break;
223                 }
224                 PDEBUGG("Trying %li megs (at %p, %p)\n", trial_size>>20,
225                         (void *)__pa(high_memory), remapped);
226                 for (i = last_trial; i < trial_size; i += 16) {
227                         strcpy((char *)(remapped)+i, test_string);
228                         if (strcmp((char *)(remapped)+i, test_string))
229                                 break;
230                         }
231                 iounmap((void *)remapped);
232                 schedule();
233                 last_trial = trial_size;
234                 if (i == trial_size)
235                         trial_size += step; /* increment, if all went well */
236                 else {
237                         PDEBUGG("%li megs copy test failed!\n", trial_size>>20);
238                         break;
239                 }
240                 if (!allocator_probe)
241                         break;
242         }
243         PDEBUG("%li megs (%li k, %li b)\n", i>>20, i>>10, i);
244         allocator_buffer_size = i>>10; /* kilobytes */
245         allocator_buffer = __pa(high_memory);
246         if (!allocator_buffer_size) {
247                 printk(KERN_WARNING ALL_MSG "no free high memory to use\n");
248                 return -ENOMEM;
249         }
250
251         /*
252         * to simplify things, always have two cells in the list:
253         * the first and the last. This avoids some conditionals and
254         * extra code when allocating and deallocating: we only play
255         * in the middle of the list
256         */
257         head = kmalloc(sizeof(struct allocator_struct), GFP_KERNEL);
258         if (!head)
259                 return -ENOMEM;
260         tail = kmalloc(sizeof(struct allocator_struct), GFP_KERNEL);
261         if (!tail) {
262                 kfree(head);
263                 return -ENOMEM;
264         }
265
266         max = allocator_buffer_size<<10;
267
268         head->size = tail->size = 0;
269         head->address = allocator_buffer;
270         tail->address = allocator_buffer + max;
271         head->next = tail;
272         tail->next = NULL;
273         allocator_list = head;
274
275         /* Back to the user code, in KB */
276         *allocator_max = allocator_buffer_size;
277
278         return 0; /* ok, ready */
279 }
280
281 void allocator_cleanup(void)
282 {
283         struct allocator_struct *ptr, *next;
284
285         for (ptr = allocator_list; ptr; ptr = next) {
286                 next = ptr->next;
287                 PDEBUG("freeing list: 0x%08lx\n", ptr->address);
288                 kfree(ptr);
289         }
290
291         allocator_buffer      = 0;
292         allocator_buffer_size = 0;
293         allocator_list = NULL;
294 }
295
296