diff options
Diffstat (limited to 'target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c')
-rw-r--r-- | target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c b/target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c new file mode 100644 index 000000000..5a10c2938 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mm/ocm-alloc.c @@ -0,0 +1,487 @@ +/* + * arch/ubicom32/mm/ocm-alloc.c + * OCM allocator for Uibcom32 On-Chip memory + * + * (C) Copyright 2009, Ubicom, Inc. + * Copyright 2004-2008 Analog Devices Inc. + * + * Based on: + * + * arch/blackfin/mm/sram-alloc.c + * + * + * This file is part of the Ubicom32 Linux Kernel Port. + * + * The Ubicom32 Linux Kernel Port is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * The Ubicom32 Linux Kernel Port is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Ubicom32 Linux Kernel Port. If not, + * see <http://www.gnu.org/licenses/>. + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/miscdevice.h> +#include <linux/ioport.h> +#include <linux/fcntl.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/mutex.h> +#include <linux/rtc.h> +#include <asm/ocm-alloc.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt, a...) +#endif +/* + * the data structure for OCM heap pieces + */ +struct ocm_piece { + void *paddr; + int size; + pid_t pid; + struct ocm_piece *next; +}; + +/* + * struct ocm_heap + */ +struct ocm_heap { + struct ocm_piece free_head; + struct ocm_piece used_head; + struct mutex lock; +}; + +static struct ocm_heap ocm_inst_heap; +int ubi32_ocm_skbuf_max = 21, ubi32_ocm_skbuf, ubi32_ddr_skbuf; + +/* + * OCM area for storing code + */ +extern asmlinkage void *__ocm_free_begin; +extern asmlinkage void *__ocm_free_end; +extern asmlinkage void *__ocm_inst_heap_begin; +extern asmlinkage void *__ocm_inst_heap_end; +#define OCM_INST_HEAP_BEGIN ((unsigned int)&__ocm_inst_heap_begin) +#define OCM_INST_HEAP_END ((unsigned int)&__ocm_inst_heap_end) +#define OCM_INST_HEAP_LENGTH (OCM_INST_HEAP_END - OCM_INST_HEAP_BEGIN) + +static struct kmem_cache *ocm_piece_cache; + +/* + * _ocm_heap_init() + */ +static int __init _ocm_heap_init(struct ocm_heap *ocmh, + unsigned int start, + unsigned int size) +{ + ocmh->free_head.next = kmem_cache_alloc(ocm_piece_cache, GFP_KERNEL); + + if (!ocmh->free_head.next) + return -1; + + ocmh->free_head.next->paddr = (void *)start; + ocmh->free_head.next->size = size; + ocmh->free_head.next->pid = 0; + ocmh->free_head.next->next = 0; + + ocmh->used_head.next = NULL; + + /* mutex initialize */ + mutex_init(&ocmh->lock); + + return 0; +} + +/* + * _ocm_alloc_init() + * + * starts the ocm heap(s) + */ +static int __init _ocm_alloc_init(void) +{ + if (OCM_INST_HEAP_LENGTH) { + ocm_piece_cache = kmem_cache_create("ocm_piece_cache", + sizeof(struct ocm_piece), + 0, SLAB_PANIC, NULL); + + if (_ocm_heap_init(&ocm_inst_heap, + OCM_INST_HEAP_BEGIN, + OCM_INST_HEAP_LENGTH) == 0) + printk(KERN_INFO "OCM Instruction Heap %d KB\n", + OCM_INST_HEAP_LENGTH >> 10); + else + printk(KERN_INFO "Failed to initialize OCM " + "Instruction Heap\n"); + + } else + printk(KERN_INFO "No space available for OCM " + "Instruction Heap\n"); + + return 0; +} +pure_initcall(_ocm_alloc_init); + +/* + * _ocm_alloc() + * generic alloc a block in the ocm heap, if successful + * returns the pointer. + */ +static void *_ocm_alloc(size_t size, pid_t pid, struct ocm_heap *ocmheap) +{ + struct ocm_piece *pslot, *plast, *pavail; + struct ocm_piece *pfree_head = &ocmheap->free_head; + struct ocm_piece *pused_head = &ocmheap->used_head; + + if (size <= 0 || !pfree_head || !pused_head) + return NULL; + + /* Align the size */ + size = (size + 3) & ~3; + + pslot = pfree_head->next; + plast = pfree_head; + + /* + * search an available piece slot + */ + while (pslot != NULL && size > pslot->size) { + plast = pslot; + pslot = pslot->next; + } + + if (!pslot) + return NULL; + + if (pslot->size == size) { + /* + * Unlink this block from the list + */ + plast->next = pslot->next; + pavail = pslot; + } else { + /* + * Split this block in two. + */ + pavail = kmem_cache_alloc(ocm_piece_cache, GFP_KERNEL); + + if (!pavail) + return NULL; + + pavail->paddr = pslot->paddr; + pavail->size = size; + pslot->paddr += size; + pslot->size -= size; + } + + pavail->pid = pid; + + pslot = pused_head->next; + plast = pused_head; + + /* + * insert new piece into used piece list !!! + */ + while (pslot != NULL && pavail->paddr < pslot->paddr) { + plast = pslot; + pslot = pslot->next; + } + + pavail->next = pslot; + plast->next = pavail; + + DEBUGP("_ocm_alloc %d bytes at %p from in %p", + size, pavail->paddr, ocmheap); + + return pavail->paddr; +} + +#if 0 +/* Allocate the largest available block. */ +static void *_ocm_alloc_max(struct ocm_heap *ocmheap, + unsigned long *psize) +{ + struct ocm_piece *pfree_head = &ocmheap->free_head; + struct ocm_piece *pslot, *pmax; + + pmax = pslot = pfree_head->next; + + /* search an available piece slot */ + while (pslot != NULL) { + if (pslot->size > pmax->size) + pmax = pslot; + pslot = pslot->next; + } + + if (!pmax) + return NULL; + + *psize = pmax->size; + + return _ocm_alloc(*psize, ocmheap); +} +#endif + +/* + * _ocm_free() + * generic free a block in the ocm heap, if successful + */ +static int _ocm_free(const void *addr, + struct ocm_heap *ocmheap) +{ + struct ocm_piece *pslot, *plast, *pavail; + struct ocm_piece *pfree_head = &ocmheap->free_head; + struct ocm_piece *pused_head = &ocmheap->used_head; + + /* search the relevant memory slot */ + pslot = pused_head->next; + plast = pused_head; + + /* search an available piece slot */ + while (pslot != NULL && pslot->paddr != addr) { + plast = pslot; + pslot = pslot->next; + } + + if (!pslot) { + DEBUGP("_ocm_free %p not found in %p", addr, ocmheap); + return -1; + } + DEBUGP("_ocm_free %p from in %p", addr, ocmheap); + + plast->next = pslot->next; + pavail = pslot; + pavail->pid = 0; + + /* insert free pieces back to the free list */ + pslot = pfree_head->next; + plast = pfree_head; + + while (pslot != NULL && addr > pslot->paddr) { + plast = pslot; + pslot = pslot->next; + } + + if (plast != pfree_head && + plast->paddr + plast->size == pavail->paddr) { + plast->size += pavail->size; + kmem_cache_free(ocm_piece_cache, pavail); + } else { + pavail->next = plast->next; + plast->next = pavail; + plast = pavail; + } + + if (pslot && plast->paddr + plast->size == pslot->paddr) { + plast->size += pslot->size; + plast->next = pslot->next; + kmem_cache_free(ocm_piece_cache, pslot); + } + + return 0; +} + +/* + * ocm_inst_alloc() + * + * allocates a block of size in the ocm instrction heap, if + * successful returns address allocated. + */ +void *ocm_inst_alloc(size_t size, pid_t pid) +{ + void *addr; + + if (!OCM_INST_HEAP_LENGTH) + return NULL; + + + mutex_lock(&ocm_inst_heap.lock); + + addr = _ocm_alloc(size, pid, &ocm_inst_heap); + + mutex_unlock(&ocm_inst_heap.lock); + + return addr; +} +EXPORT_SYMBOL(ocm_inst_alloc); + +/* + * ocm_inst_free() + * free a block in the ocm instrction heap, returns 0 if successful. + */ +int ocm_inst_free(const void *addr) +{ + int ret; + + if (!OCM_INST_HEAP_LENGTH) + return -1; + + mutex_lock(&ocm_inst_heap.lock); + + ret = _ocm_free(addr, &ocm_inst_heap); + + mutex_unlock(&ocm_inst_heap.lock); + + return ret; +} +EXPORT_SYMBOL(ocm_inst_free); + +/* + * ocm_free() + * free a block in one of the ocm heaps, returns 0 if successful. + */ +int ocm_free(const void *addr) +{ + if (addr >= (void *)OCM_INST_HEAP_BEGIN + && addr < (void *)(OCM_INST_HEAP_END)) + return ocm_inst_free(addr); + else + return -1; +} +EXPORT_SYMBOL(ocm_free); + + +#ifdef CONFIG_PROC_FS +/* Need to keep line of output the same. Currently, that is 46 bytes + * (including newline). + */ +static int _ocm_proc_read(char *buf, int *len, int count, const char *desc, + struct ocm_heap *ocmheap) +{ + struct ocm_piece *pslot; + struct ocm_piece *pfree_head = &ocmheap->free_head; + struct ocm_piece *pused_head = &ocmheap->used_head; + + /* The format is the following + * --- OCM 123456789012345 Size PID State \n + * 12345678-12345678 1234567890 12345 1234567890\n + */ + int l; + l = sprintf(&buf[*len], "--- OCM %-15s Size PID State \n", + desc); + + *len += l; + count -= l; + + mutex_lock(&ocm_inst_heap.lock); + + /* + * search the relevant memory slot + */ + pslot = pused_head->next; + + while (pslot != NULL && count > 46) { + l = sprintf(&buf[*len], "%p-%p %10i %5i %-10s\n", + pslot->paddr, pslot->paddr + pslot->size, + pslot->size, pslot->pid, "ALLOCATED"); + + *len += l; + count -= l; + pslot = pslot->next; + } + + pslot = pfree_head->next; + + while (pslot != NULL && count > 46) { + l = sprintf(&buf[*len], "%p-%p %10i %5i %-10s\n", + pslot->paddr, pslot->paddr + pslot->size, + pslot->size, pslot->pid, "FREE"); + + *len += l; + count -= l; + pslot = pslot->next; + } + + mutex_unlock(&ocm_inst_heap.lock); + + return 0; +} + +static int ocm_proc_read(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + int len = 0; + + len = sprintf(&buf[len], "--- OCM SKB usage (max RX buf %d)\n" + "(SKB in OCM) %d - (SKB in DDR) %d\n", + ubi32_ocm_skbuf_max, + ubi32_ocm_skbuf, + ubi32_ddr_skbuf); + + len += sprintf(&buf[len], "--- OCM Data Heap Size\n" + "%p-%p %10i\n", + ((void *)&__ocm_free_begin), + ((void *)&__ocm_free_end), + ((unsigned int)&__ocm_free_end) - + ((unsigned int)&__ocm_free_begin)); + + if (_ocm_proc_read(buf, &len, count - len, "Inst Heap", + &ocm_inst_heap)) + goto not_done; + *eof = 1; + not_done: + return len; +} + +static int ocm_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int n, v; + char in[8]; + + if (count > sizeof(in)) + return -EINVAL; + + if (copy_from_user(in, buffer, count)) + return -EFAULT; + in[count-1] = 0; + + printk(KERN_INFO "OCM skb alloc max = %s\n", in); + + n = 0; + v = 0; + while ((in[n] >= '0') && (in[n] <= '9')) { + v = v * 10 + (int)(in[n] - '0'); + n++; + } + + if (v == 0) + return -EINVAL; + + ubi32_ocm_skbuf_max = v; + ubi32_ocm_skbuf = ubi32_ddr_skbuf = 0; + + return count; +} + +static int __init sram_proc_init(void) +{ + struct proc_dir_entry *ptr; + ptr = create_proc_entry("ocm", S_IFREG | S_IRUGO, NULL); + if (!ptr) { + printk(KERN_WARNING "unable to create /proc/ocm\n"); + return -1; + } + ptr->read_proc = ocm_proc_read; + ptr->write_proc = ocm_proc_write; + return 0; +} +late_initcall(sram_proc_init); +#endif |