diff options
author | James Hogan <james.hogan@imgtec.com> | 2012-10-09 10:54:17 +0100 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-03-02 20:09:21 +0000 |
commit | c438b58e65462cfff172b396d03d6bc45c971fca (patch) | |
tree | 3d8ea3cfbe75f4e6b41f5f41f36c889df5b0f7d8 /arch/metag/kernel | |
parent | bbc17704d5d3a8e4325dae74c2a2e77218c26057 (diff) |
metag: TCM support
Add some TCM support
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Diffstat (limited to 'arch/metag/kernel')
-rw-r--r-- | arch/metag/kernel/tcm.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/arch/metag/kernel/tcm.c b/arch/metag/kernel/tcm.c new file mode 100644 index 000000000000..5d102b31ce84 --- /dev/null +++ b/arch/metag/kernel/tcm.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2010 Imagination Technologies Ltd. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/stddef.h> +#include <linux/genalloc.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <asm/page.h> +#include <asm/tcm.h> + +struct tcm_pool { + struct list_head list; + unsigned int tag; + unsigned long start; + unsigned long end; + struct gen_pool *pool; +}; + +static LIST_HEAD(pool_list); + +static struct tcm_pool *find_pool(unsigned int tag) +{ + struct list_head *lh; + struct tcm_pool *pool; + + list_for_each(lh, &pool_list) { + pool = list_entry(lh, struct tcm_pool, list); + if (pool->tag == tag) + return pool; + } + + return NULL; +} + +/** + * tcm_alloc - allocate memory from a TCM pool + * @tag: tag of the pool to allocate memory from + * @len: number of bytes to be allocated + * + * Allocate the requested number of bytes from the pool matching + * the specified tag. Returns the address of the allocated memory + * or zero on failure. + */ +unsigned long tcm_alloc(unsigned int tag, size_t len) +{ + unsigned long vaddr; + struct tcm_pool *pool; + + pool = find_pool(tag); + if (!pool) + return 0; + + vaddr = gen_pool_alloc(pool->pool, len); + if (!vaddr) + return 0; + + return vaddr; +} + +/** + * tcm_free - free a block of memory to a TCM pool + * @tag: tag of the pool to free memory to + * @addr: address of the memory to be freed + * @len: number of bytes to be freed + * + * Free the requested number of bytes at a specific address to the + * pool matching the specified tag. + */ +void tcm_free(unsigned int tag, unsigned long addr, size_t len) +{ + struct tcm_pool *pool; + + pool = find_pool(tag); + if (!pool) + return; + gen_pool_free(pool->pool, addr, len); +} + +/** + * tcm_lookup_tag - find the tag matching an address + * @p: memory address to lookup the tag for + * + * Find the tag of the tcm memory region that contains the + * specified address. Returns %TCM_INVALID_TAG if no such + * memory region could be found. + */ +unsigned int tcm_lookup_tag(unsigned long p) +{ + struct list_head *lh; + struct tcm_pool *pool; + unsigned long addr = (unsigned long) p; + + list_for_each(lh, &pool_list) { + pool = list_entry(lh, struct tcm_pool, list); + if (addr >= pool->start && addr < pool->end) + return pool->tag; + } + + return TCM_INVALID_TAG; +} + +/** + * tcm_add_region - add a memory region to TCM pool list + * @reg: descriptor of region to be added + * + * Add a region of memory to the TCM pool list. Returns 0 on success. + */ +int __init tcm_add_region(struct tcm_region *reg) +{ + struct tcm_pool *pool; + + pool = kmalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) { + pr_err("Failed to alloc memory for TCM pool!\n"); + return -ENOMEM; + } + + pool->tag = reg->tag; + pool->start = reg->res.start; + pool->end = reg->res.end; + + /* + * 2^3 = 8 bytes granularity to allow for 64bit access alignment. + * -1 = NUMA node specifier. + */ + pool->pool = gen_pool_create(3, -1); + + if (!pool->pool) { + pr_err("Failed to create TCM pool!\n"); + kfree(pool); + return -ENOMEM; + } + + if (gen_pool_add(pool->pool, reg->res.start, + reg->res.end - reg->res.start + 1, -1)) { + pr_err("Failed to add memory to TCM pool!\n"); + return -ENOMEM; + } + pr_info("Added %s TCM pool (%08x bytes @ %08x)\n", + reg->res.name, reg->res.end - reg->res.start + 1, + reg->res.start); + + list_add_tail(&pool->list, &pool_list); + + return 0; +} |