disable sstrip when using musl
[lede.git] / target / linux / ubicom32 / files / arch / ubicom32 / mm / ocm-alloc.c
1 /*
2  * arch/ubicom32/mm/ocm-alloc.c
3  *      OCM allocator for Uibcom32 On-Chip memory
4  *
5  * (C) Copyright 2009, Ubicom, Inc.
6  *  Copyright 2004-2008 Analog Devices Inc.
7  *
8  *  Based on:
9  *
10  *  arch/blackfin/mm/sram-alloc.c
11  *
12  *
13  * This file is part of the Ubicom32 Linux Kernel Port.
14  *
15  * The Ubicom32 Linux Kernel Port is free software: you can redistribute
16  * it and/or modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation, either version 2 of the
18  * License, or (at your option) any later version.
19  *
20  * The Ubicom32 Linux Kernel Port is distributed in the hope that it
21  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
22  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
23  * the GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with the Ubicom32 Linux Kernel Port.  If not,
27  * see <http://www.gnu.org/licenses/>.
28  *
29  * Ubicom32 implementation derived from (with many thanks):
30  *   arch/m68knommu
31  *   arch/blackfin
32  *   arch/parisc
33  */
34
35 #include <linux/module.h>
36 #include <linux/kernel.h>
37 #include <linux/types.h>
38 #include <linux/miscdevice.h>
39 #include <linux/ioport.h>
40 #include <linux/fcntl.h>
41 #include <linux/init.h>
42 #include <linux/poll.h>
43 #include <linux/proc_fs.h>
44 #include <linux/mutex.h>
45 #include <linux/rtc.h>
46 #include <asm/ocm-alloc.h>
47
48 #if 0
49 #define DEBUGP printk
50 #else
51 #define DEBUGP(fmt, a...)
52 #endif
53 /*
54  * the data structure for OCM heap pieces
55  */
56 struct ocm_piece {
57         void *paddr;
58         int size;
59         pid_t pid;
60         struct ocm_piece *next;
61 };
62
63 /*
64  * struct ocm_heap
65  */
66 struct ocm_heap {
67         struct ocm_piece free_head;
68         struct ocm_piece used_head;
69         struct mutex lock;
70 };
71
72 static struct ocm_heap ocm_inst_heap;
73 int ubi32_ocm_skbuf_max = 21, ubi32_ocm_skbuf, ubi32_ddr_skbuf;
74
75 /*
76  * OCM area for storing code
77  */
78 extern asmlinkage void *__ocm_free_begin;
79 extern asmlinkage void *__ocm_free_end;
80 extern asmlinkage void *__ocm_inst_heap_begin;
81 extern asmlinkage void *__ocm_inst_heap_end;
82 #define OCM_INST_HEAP_BEGIN ((unsigned int)&__ocm_inst_heap_begin)
83 #define OCM_INST_HEAP_END ((unsigned int)&__ocm_inst_heap_end)
84 #define OCM_INST_HEAP_LENGTH (OCM_INST_HEAP_END - OCM_INST_HEAP_BEGIN)
85
86 static struct kmem_cache *ocm_piece_cache;
87
88 /*
89  * _ocm_heap_init()
90  */
91 static int __init _ocm_heap_init(struct ocm_heap *ocmh,
92                                   unsigned int start,
93                                   unsigned int size)
94 {
95         ocmh->free_head.next = kmem_cache_alloc(ocm_piece_cache, GFP_KERNEL);
96
97         if (!ocmh->free_head.next)
98                 return -1;
99
100         ocmh->free_head.next->paddr = (void *)start;
101         ocmh->free_head.next->size = size;
102         ocmh->free_head.next->pid = 0;
103         ocmh->free_head.next->next = 0;
104
105         ocmh->used_head.next = NULL;
106
107         /* mutex initialize */
108         mutex_init(&ocmh->lock);
109
110         return 0;
111 }
112
113 /*
114  * _ocm_alloc_init()
115  *
116  * starts the ocm heap(s)
117  */
118 static int __init _ocm_alloc_init(void)
119 {
120         if (OCM_INST_HEAP_LENGTH) {
121                 ocm_piece_cache = kmem_cache_create("ocm_piece_cache",
122                                                     sizeof(struct ocm_piece),
123                                                     0, SLAB_PANIC, NULL);
124
125                 if (_ocm_heap_init(&ocm_inst_heap,
126                                    OCM_INST_HEAP_BEGIN,
127                                    OCM_INST_HEAP_LENGTH) == 0)
128                         printk(KERN_INFO "OCM Instruction Heap %d KB\n",
129                                OCM_INST_HEAP_LENGTH >> 10);
130                 else
131                         printk(KERN_INFO "Failed to initialize OCM "
132                                "Instruction Heap\n");
133
134         } else
135                 printk(KERN_INFO "No space available for OCM "
136                        "Instruction Heap\n");
137
138         return 0;
139 }
140 pure_initcall(_ocm_alloc_init);
141
142 /*
143  * _ocm_alloc()
144  *      generic alloc a block in the ocm heap, if successful
145  *      returns the pointer.
146  */
147 static void *_ocm_alloc(size_t size, pid_t pid, struct ocm_heap *ocmheap)
148 {
149         struct ocm_piece *pslot, *plast, *pavail;
150         struct ocm_piece *pfree_head = &ocmheap->free_head;
151         struct ocm_piece *pused_head = &ocmheap->used_head;
152
153         if (size <= 0 || !pfree_head || !pused_head)
154                 return NULL;
155
156         /* Align the size */
157         size = (size + 3) & ~3;
158
159         pslot = pfree_head->next;
160         plast = pfree_head;
161
162         /*
163          * search an available piece slot
164          */
165         while (pslot != NULL && size > pslot->size) {
166                 plast = pslot;
167                 pslot = pslot->next;
168         }
169
170         if (!pslot)
171                 return NULL;
172
173         if (pslot->size == size) {
174                 /*
175                  * Unlink this block from the list
176                  */
177                 plast->next = pslot->next;
178                 pavail = pslot;
179         } else {
180                 /*
181                  * Split this block in two.
182                  */
183                 pavail = kmem_cache_alloc(ocm_piece_cache, GFP_KERNEL);
184
185                 if (!pavail)
186                         return NULL;
187
188                 pavail->paddr = pslot->paddr;
189                 pavail->size = size;
190                 pslot->paddr += size;
191                 pslot->size -= size;
192         }
193
194         pavail->pid = pid;
195
196         pslot = pused_head->next;
197         plast = pused_head;
198
199         /*
200          * insert new piece into used piece list !!!
201          */
202         while (pslot != NULL && pavail->paddr < pslot->paddr) {
203                 plast = pslot;
204                 pslot = pslot->next;
205         }
206
207         pavail->next = pslot;
208         plast->next = pavail;
209
210         DEBUGP("_ocm_alloc %d bytes at %p from in %p",
211                size, pavail->paddr, ocmheap);
212
213         return pavail->paddr;
214 }
215
216 #if 0
217 /* Allocate the largest available block.  */
218 static void *_ocm_alloc_max(struct ocm_heap *ocmheap,
219                              unsigned long *psize)
220 {
221         struct ocm_piece *pfree_head = &ocmheap->free_head;
222         struct ocm_piece *pslot, *pmax;
223
224         pmax = pslot = pfree_head->next;
225
226         /* search an available piece slot */
227         while (pslot != NULL) {
228                 if (pslot->size > pmax->size)
229                         pmax = pslot;
230                 pslot = pslot->next;
231         }
232
233         if (!pmax)
234                 return NULL;
235
236         *psize = pmax->size;
237
238         return _ocm_alloc(*psize, ocmheap);
239 }
240 #endif
241
242 /*
243  * _ocm_free()
244  *      generic free a block in the ocm heap, if successful
245  */
246 static int _ocm_free(const void *addr,
247                      struct ocm_heap *ocmheap)
248 {
249         struct ocm_piece *pslot, *plast, *pavail;
250         struct ocm_piece *pfree_head = &ocmheap->free_head;
251         struct ocm_piece *pused_head = &ocmheap->used_head;
252
253         /* search the relevant memory slot */
254         pslot = pused_head->next;
255         plast = pused_head;
256
257         /* search an available piece slot */
258         while (pslot != NULL && pslot->paddr != addr) {
259                 plast = pslot;
260                 pslot = pslot->next;
261         }
262
263         if (!pslot) {
264                 DEBUGP("_ocm_free %p  not found in %p", addr, ocmheap);
265                 return -1;
266         }
267         DEBUGP("_ocm_free %p from in %p", addr, ocmheap);
268
269         plast->next = pslot->next;
270         pavail = pslot;
271         pavail->pid = 0;
272
273         /* insert free pieces back to the free list */
274         pslot = pfree_head->next;
275         plast = pfree_head;
276
277         while (pslot != NULL && addr > pslot->paddr) {
278                 plast = pslot;
279                 pslot = pslot->next;
280         }
281
282         if (plast != pfree_head &&
283             plast->paddr + plast->size == pavail->paddr) {
284                 plast->size += pavail->size;
285                 kmem_cache_free(ocm_piece_cache, pavail);
286         } else {
287                 pavail->next = plast->next;
288                 plast->next = pavail;
289                 plast = pavail;
290         }
291
292         if (pslot && plast->paddr + plast->size == pslot->paddr) {
293                 plast->size += pslot->size;
294                 plast->next = pslot->next;
295                 kmem_cache_free(ocm_piece_cache, pslot);
296         }
297
298         return 0;
299 }
300
301 /*
302  * ocm_inst_alloc()
303  *
304  *      allocates a block of size in the ocm instrction heap, if
305  *      successful returns address allocated.
306  */
307 void *ocm_inst_alloc(size_t size, pid_t pid)
308 {
309         void *addr;
310
311         if (!OCM_INST_HEAP_LENGTH)
312                 return NULL;
313
314
315         mutex_lock(&ocm_inst_heap.lock);
316
317         addr = _ocm_alloc(size, pid, &ocm_inst_heap);
318
319         mutex_unlock(&ocm_inst_heap.lock);
320
321         return addr;
322 }
323 EXPORT_SYMBOL(ocm_inst_alloc);
324
325 /*
326  * ocm_inst_free()
327  *      free a block in the ocm instrction heap, returns 0 if successful.
328  */
329 int ocm_inst_free(const void *addr)
330 {
331         int ret;
332
333         if (!OCM_INST_HEAP_LENGTH)
334                 return -1;
335
336         mutex_lock(&ocm_inst_heap.lock);
337
338         ret = _ocm_free(addr, &ocm_inst_heap);
339
340         mutex_unlock(&ocm_inst_heap.lock);
341
342         return ret;
343 }
344 EXPORT_SYMBOL(ocm_inst_free);
345
346 /*
347  * ocm_free()
348  *      free a block in one of the ocm heaps, returns 0 if successful.
349  */
350 int ocm_free(const void *addr)
351 {
352         if (addr >= (void *)OCM_INST_HEAP_BEGIN
353                  && addr < (void *)(OCM_INST_HEAP_END))
354                 return ocm_inst_free(addr);
355         else
356                 return -1;
357 }
358 EXPORT_SYMBOL(ocm_free);
359
360
361 #ifdef CONFIG_PROC_FS
362 /* Need to keep line of output the same.  Currently, that is 46 bytes
363  * (including newline).
364  */
365 static int _ocm_proc_read(char *buf, int *len, int count, const char *desc,
366                            struct ocm_heap *ocmheap)
367 {
368         struct ocm_piece *pslot;
369         struct ocm_piece *pfree_head = &ocmheap->free_head;
370         struct ocm_piece *pused_head = &ocmheap->used_head;
371
372         /* The format is the following
373          * --- OCM 123456789012345 Size   PID State     \n
374          * 12345678-12345678 1234567890 12345 1234567890\n
375          */
376         int l;
377         l = sprintf(&buf[*len], "--- OCM %-15s Size   PID State     \n",
378                     desc);
379
380         *len += l;
381         count -= l;
382
383         mutex_lock(&ocm_inst_heap.lock);
384
385         /*
386          * search the relevant memory slot
387          */
388         pslot = pused_head->next;
389
390         while (pslot != NULL && count > 46) {
391                 l = sprintf(&buf[*len], "%p-%p %10i %5i %-10s\n",
392                              pslot->paddr, pslot->paddr + pslot->size,
393                              pslot->size, pslot->pid, "ALLOCATED");
394
395                 *len += l;
396                 count -= l;
397                 pslot = pslot->next;
398         }
399
400         pslot = pfree_head->next;
401
402         while (pslot != NULL && count > 46) {
403                 l = sprintf(&buf[*len], "%p-%p %10i %5i %-10s\n",
404                             pslot->paddr, pslot->paddr + pslot->size,
405                             pslot->size, pslot->pid, "FREE");
406
407                 *len += l;
408                 count -= l;
409                 pslot = pslot->next;
410         }
411
412         mutex_unlock(&ocm_inst_heap.lock);
413
414         return 0;
415 }
416
417 static int ocm_proc_read(char *buf, char **start, off_t offset, int count,
418                 int *eof, void *data)
419 {
420         int len = 0;
421
422         len = sprintf(&buf[len], "--- OCM SKB usage (max RX buf %d)\n"
423                         "(SKB in OCM) %d - (SKB in DDR) %d\n",
424                         ubi32_ocm_skbuf_max,
425                         ubi32_ocm_skbuf,
426                         ubi32_ddr_skbuf);
427
428         len += sprintf(&buf[len], "--- OCM Data Heap       Size\n"
429                         "%p-%p %10i\n",
430                         ((void *)&__ocm_free_begin),
431                         ((void *)&__ocm_free_end),
432                         ((unsigned int)&__ocm_free_end) -
433                         ((unsigned int)&__ocm_free_begin));
434
435         if (_ocm_proc_read(buf, &len, count - len, "Inst Heap",
436                             &ocm_inst_heap))
437                 goto not_done;
438         *eof = 1;
439  not_done:
440         return len;
441 }
442
443 static int ocm_proc_write(struct file *file, const char __user *buffer,
444                            unsigned long count, void *data)
445 {
446         int n, v;
447         char in[8];
448
449         if (count > sizeof(in))
450                 return -EINVAL;
451
452         if (copy_from_user(in, buffer, count))
453                 return -EFAULT;
454         in[count-1] = 0;
455
456         printk(KERN_INFO "OCM skb alloc max = %s\n", in);
457
458         n = 0;
459         v = 0;
460         while ((in[n] >= '0') && (in[n] <= '9')) {
461                 v = v * 10 + (int)(in[n] - '0');
462                 n++;
463         }
464
465         if (v == 0)
466                 return -EINVAL;
467
468         ubi32_ocm_skbuf_max = v;
469         ubi32_ocm_skbuf = ubi32_ddr_skbuf = 0;
470
471         return count;
472 }
473
474 static int __init sram_proc_init(void)
475 {
476         struct proc_dir_entry *ptr;
477         ptr = create_proc_entry("ocm", S_IFREG | S_IRUGO, NULL);
478         if (!ptr) {
479                 printk(KERN_WARNING "unable to create /proc/ocm\n");
480                 return -1;
481         }
482         ptr->read_proc = ocm_proc_read;
483         ptr->write_proc = ocm_proc_write;
484         return 0;
485 }
486 late_initcall(sram_proc_init);
487 #endif